簡體   English   中英

如何使用 JPA 和 Hibernate 防止 SQL 注入?

[英]How to prevent SQL Injection with JPA and 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 注入發生在流氓攻擊者可以操縱查詢構建過程以便他可以執行與應用程序開發人員最初預期不同的 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) ))]

JPQL 注入

使用 JPQL 或 HQL 查詢時也可能發生 SQL 注入,如以下示例所示:

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)
    )
);


return entityManager.createQuery(query).getResultList();

我想在這里添加一個特殊的 SQL 注入,可以在搜索中使用 Like 查詢。

假設我們有一個查詢字符串,如下所示:

queryString = queryString + " and c.name like :name";

在設置 name 參數時,大多數人通常會使用它。

query.setParameter("name", "%" + name + "%");

現在,如上所述,像“ 1=1 ”這樣的傳統參數不能被注入,因為 TypedQuery 和 Hibernate 將默認處理它。

但是這里可能存在特殊的 SQL 注入,這是因為使用下划線的 LIKE 查詢結構

下划線通配符用於匹配 MySQL 中的一個字符的含義,例如 select * from users where user like 'abc_de'; 這將產生以 abc 開頭、以 de 結尾且中間正好有 1 個字符的用戶形式的輸出。

現在,如果在我們的場景中,如果我們設置

  • name="_"生成姓名至少為 1 個字母的客戶
  • name="__"生成姓名至少為 2 個字母的客戶
  • name="___"產生名字至少為 3 個字母的客戶

等等。

理想的修復:

為了緩解這種情況,我們需要使用前綴轉義所有下划線。

___ 將變成 \\_\\_\\_(相當於 3 個原始下划線)

同樣,反之亦然的查詢也將導致需要轉義 % 的注入。

我們應該總是盡量使用一般的存儲過程來防止 SQLInjection.. 如果存儲過程是不可能的; 我們應該嘗試准備好的語句。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM