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

时间:2012-12-31 13:45:04

标签: java sql hibernate jpa sql-injection

我正在使用hibernate开发一个应用程序。当我尝试创建一个登录页面时,出现了Sql Injection的问题。  我有以下代码:

@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 Injection?loginInfo表的create table语法如下:

create table login_info
  (user_name varchar(16) not null primary key,
  pass_word varchar(16) not null); 

6 个答案:

答案 0 :(得分:22)

Query q = sessionFactory.getCurrentSession().createQuery("from LoginInfo where userName = :name");
q.setParameter("name", userName);
List<LoginInfo> loginList = q.list();

你也有其他选择,请参阅mkyong的这个好article

答案 1 :(得分:17)

您需要使用命名参数来避免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();

答案 2 :(得分:5)

什么是SQL注入?

  

当恶意攻击者可以操纵查询时,就会发生SQL注入   构建过程,以便他可以执行与以下语句不同的SQL语句   应用程序开发人员最初的意图

如何防止SQL注入攻击

解决方案非常简单直接。您只需要确保始终使用绑定参数即可:

$ kubectl run busybox --image busybox:1.28 --restart=Never --rm -it busybox -- sh
If you don't see a command prompt, try pressing enter.
/ # nslookup kubernetes.default
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
/ # exit
pod "busybox" deleted

现在,如果有人试图破解此查询:

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

将防止SQL注入攻击:

getPostCommentByReview("1 AND 1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) )");

JPQL注入

使用JPQL或HQL查询时,也可能发生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查询不使用绑定参数,因此容易受到SQL注入的攻击。<​​/ p>

查看执行此JPQL查询时发生的情况:

public List<Post> getPostsByTitle(String title) {
    return doInJPA(entityManager -> {
        return entityManager.createQuery(
            "select p " +
            "from Post p " +
            "where" +
            "   p.title = '" + title + "'", Post.class)
        .getResultList();
    });
}

Hibernate执行以下SQL查询:

List<Post> posts = getPostsByTitle(
    "High-Performance Java Persistence' and " +
    "FUNCTION('1 >= ALL ( SELECT 1 FROM pg_locks, pg_sleep(10) ) --',) is '"
);

动态查询

您应该避免使用String串联的查询来动态构建查询:

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:[()]

如果要使用动态查询,则需要使用Criteria API:

String hql = " select e.id as id,function('getActiveUser') as name from " + domainClass.getName() + " e ";
Query query=session.createQuery(hql);
return query.list();

返回entityManager.createQuery(query).getResultList();

有关更多详细信息,请查看this article

答案 3 :(得分:0)

我们应该总是尝试使用存储过程来防止SQL注入..如果存储过程不可行;我们应该尝试准备陈述。

答案 4 :(得分:0)

  1. HQL中的位置参数

    查询hqlQuery = session.createQuery(&#34;来自订单作为订单,其中orders.id =?&#34;);

    列出结果= hqlQuery.setString(0,&#34; 123-ADB-567-QTWYTFDL&#34;)。list();

  2. HQL中的命名参数

    查询hqlQuery = session.createQuery(&#34;来自Employees as emp,其中emp.incentive&gt;:incentive&#34;);

    列出结果= hqlQuery.setLong(&#34;激励&#34;,新龙(10000))。list();

  3. HQL中的命名参数列表

    List items = new ArrayList(); items.add(&#34;书&#34); items.add(&#34;时钟&#34); items.add(&#34;油墨&#34);

    列出结果= session.createQuery(&#34;从购物车作为购物车,其中cart.item在(:itemList)&#34;)。setParameterList(&#34; itemList&#34;,items).list()

  4. HQL中的JavaBean

  5. 查询hqlQuery = session.createQuery(&#34;来自Books书籍,其中book.name =:name和book.author =:author&#34;);

    列出结果= hqlQuery.setProperties(javaBean).list();

    1. 本机SQL
    2. 查询sqlQuery = session.createSQLQuery(&#34;从书籍中选择*,作者=?&#34;);

      列出结果= sqlQuery.setString(0,&#34; Charles Dickens&#34;)。list();

答案 5 :(得分:0)

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

让我们说我们有一个查询字符串,如下所示:

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

设置name参数时,大多数人通常会使用它。

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

现在,如上所述,由于TypedQuery不能注入传统参数“ 1 = 1 ”,因此默认情况下,Hibernate将对其进行处理。

但是这里可能存在特殊的SQL注入,这是因为LIKE查询结构使用了下划线

  

下划线通配符用于精确匹配其中的一个字符   MySQL的含义,例如,从用户喜欢的用户中选择*   'abc_de';这将产生以abc开头,以end结尾的用户输出   带有de,并且之间只有1个字符。

现在,如果在我们的情况下,如果我们设置

  • name =“ _” 产生名称至少为1个字母的客户
  • name =“ __” 产生姓名至少为2个字母的客户
  • name =“ ___” 产生姓名至少为3个字母的客户

以此类推。

理想解决方案:

  

为减轻这种情况,我们需要使用前缀对所有下划线进行转义。

___将变成\ _ \ _ \ _(相当于3个原始下划线)

反之亦然,反之亦然,还会导致需要%的转义的注入。