繁体   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