Hibernateを使用してアプリケーションを開発しています。ログインページを作成しようとすると、SQLインジェクションの問題が発生します。私は次のコードを持っています:
@Component
@Transactional(propagation = Propagation.SUPPORTS)
public class LoginInfoDAOImpl implements LoginInfoDAO{
@Autowired
private SessionFactory sessionFactory;
@Override
public LoginInfo getLoginInfo(String userName,String password){
List<LoginInfo> loginList = sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName='"+userName+"' and password='"+password+"'").list();
if(loginList!=null )
return loginList.get(0);
else return null;
}
}
このシナリオでSQLインジェクションを防ぐにはどうすればよいですか?loginInfoテーブルのテーブル作成構文は次のとおりです。
create table login_info
(user_name varchar(16) not null primary key,
pass_Word varchar(16) not null);
Query q = sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName = :name");
q.setParameter("name", userName);
List<LoginInfo> loginList = q.list();
他のオプションもあります。mkyongのこの素敵な 記事 を参照してください。
SQLインジェクションを回避するには、名前付きパラメーターを使用する必要があります。また、(SQLインジェクションとは関係ありませんが、一般的にはセキュリティで)最初の結果を返さず、getSingleResultを使用するため、複数の結果がある場合何らかの理由で、クエリはNonUniqueResultExceptionで失敗し、ログインは成功しません
Query query= sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName=:userName and password= :password");
query.setParameter("username", userName);
query.setParameter("password", password);
LoginInfo loginList = (LoginInfo)query.getSingleResult();
SQLインジェクションは、不正な攻撃者がクエリ構築プロセスを操作して、アプリケーション開発者が当初意図していたものとは異なるSQLステートメントを実行できる場合に発生します
解決策は非常にシンプルで簡単です。常にバインドパラメータを必ず使用する必要があります。
public PostComment getPostCommentByReview(String review) {
return doInJPA(entityManager -> {
return entityManager.createQuery(
"select p " +
"from PostComment p " +
"where p.review = :review", PostComment.class)
.setParameter("review", review)
.getSingleResult();
});
}
さて、誰かがこのクエリをハッキングしようとしている場合:
getPostCommentByReview("1 AND 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) )");
sQLインジェクション攻撃は防止されます。
Time:1, Query:["select postcommen0_.id as id1_1_, postcommen0_.post_id as post_id3_1_, postcommen0_.review as review2_1_ from post_comment postcommen0_ where postcommen0_.review=?"], Params:[(1 AND 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ))]
SQLインジェクションは、次の例に示すように、JPQLまたはHQLクエリを使用するときにも発生する可能性があります。
public List<Post> getPostsByTitle(String title) {
return doInJPA(entityManager -> {
return entityManager.createQuery(
"select p " +
"from Post p " +
"where" +
" p.title = '" + title + "'", Post.class)
.getResultList();
});
}
上記のJPQLクエリはバインドパラメータを使用しないため、SQLインジェクションに対して脆弱です。
このJPQLクエリを次のように実行するとどうなるか確認してください。
List<Post> posts = getPostsByTitle(
"High-Performance Java Persistence' and " +
"FUNCTION('1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ) --',) is '"
);
Hibernateは次のSQLクエリを実行します。
Time:10003, QuerySize:1, BatchSize:0, Query:["select p.id as id1_0_, p.title as title2_0_ from post p where p.title='High-Performance Java Persistence' and 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ) --()=''"], Params:[()]
クエリを動的に構築するために文字列連結を使用するクエリは避けてください。
String hql = " select e.id as id,function('getActiveUser') as name from " + domainClass.getName() + " e ";
Query query=session.createQuery(hql);
return query.list();
動的クエリを使用する場合は、代わりにCriteria APIを使用する必要があります。
Class<Post> entityClass = Post.class;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<?> root = query.from(entityClass);
query.select(
cb.Tuple(
root.get("id"),
cb.function("now", Date.class)
)
);
entityManager.createQuery(query).getResultList();を返します。
詳しくは この記事 をご覧ください。
検索でLikeクエリを使用することで可能になる、独特のSQLインジェクションであるここに追加します。
次のようなクエリ文字列があるとします。
queryString = queryString + " and c.name like :name";
Nameパラメータを設定している間、ほとんどは一般的にこれを使用します。
query.setParameter("name", "%" + name + "%");
さて、上記のように、TypedQueryとHibernateがデフォルトで処理するため、 "1 = 1"のような従来のパラメータは注入できません。
しかし、ここでは特殊なSQLインジェクションが可能です。これは、LIKEクエリ構造がアンダースコアを使用しているためです。
アンダースコアのワイルドカードは、MySQLの意味で正確に1文字に一致させるために使用されます。たとえば、「* from users with 'abc_de';これは、abcで始まり、deで終わり、間にちょうど1文字が含まれるユーザーとして出力を生成します。
今、私たちのシナリオの場合、設定すると
等々。
理想的な修正:
これを緩和するには、すべてのアンダースコアをプレフィックスでエスケープする必要があります。
___は\ _\_\_になります(3つのアンダースコアに相当)
同様に、その逆のクエリでも、%をエスケープする必要があるインジェクションが発生します。
SQLInjectionを防ぐために、常に一般的にストアドプロシージャを使用するようにしてください。ストアドプロシージャが不可能な場合。準備済みステートメントを試す必要があります。
HQLの位置パラメーター
クエリhqlQuery = session.createQuery( "注文としての注文Orders.id =?");
結果のリスト= hqlQuery.setString(0、 "123-ADB-567-QTWYTFDL")。list();
HQLの名前付きパラメーター
Query hqlQuery = session.createQuery( "from Employees as emp where emp.incentive>:incentive");
結果のリスト= hqlQuery.setLong( "incentive"、new Long(10000))。list();
HQLの名前付きパラメーターリスト
リストアイテム= new ArrayList(); items.add( "book"); items.add( "clock"); items.add( "ink");
List results = session.createQuery( "from Cart as cart where cart.item in(:itemList)")。setParameterList( "itemList"、items).list();
HQLのJavaBean
クエリhqlQuery = session.createQuery( "from Books as books where book.name =:name and book.author =:author");
結果のリスト= hqlQuery.setProperties(javaBean).list();
Query sqlQuery = session.createSQLQuery( "Select * from Books where author =?");
結果のリスト= sqlQuery.setString(0、 "Charles Dickens")。list();