简体   繁体   English

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

[英]How to prevent SQL Injection with JPA and Hibernate?

I am developing an application using hibernate.我正在使用休眠开发应用程序。 When I try to create a Login page, The problem of Sql Injection arises.当我尝试创建登录页面时,出现了 Sql 注入的问题。 I have the following code:我有以下代码:

@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;   
          }
      }

How will i prevent Sql Injection in this scenario ?The create table syntax of loginInfo table is as follows:在这种情况下我将如何防止 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();

You have other options too, see this nice article from mkyong.您还有其他选择,请参阅 mkyong 的这篇不错的文章

You need to use named parameters to avoid sql injection.您需要使用命名参数来避免 sql 注入。 Also (nothing to do with sql injection but with security in general) do not return the first result but use getSingleResult so if there are more than one results for some reason, the query will fail with NonUniqueResultException and login will not be succesful另外(与 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();

What is SQL Injection?什么是 SQL 注入?

SQL Injection happens when a rogue attacker can manipulate the query building process so that he can execute a different SQL statement than what the application developer has originally intended SQL 注入发生在流氓攻击者可以操纵查询构建过程以便他可以执行与应用程序开发人员最初预期不同的 SQL 语句时

How to prevent the SQL injection attack如何防止SQL注入攻击

The solution is very simple and straight-forward.解决方案非常简单直接。 You just have to make sure that you always use bind parameters:您只需要确保始终使用绑定参数:

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

Now, if some is trying to hack this query:现在,如果有人试图破解这个查询:

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

the SQL Injection attack will be prevented: 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 Injection JPQL 注入

SQL Injection can also happen when using JPQL or HQL queries, as demonstrated by the following example:使用 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();
    });
}

The JPQL query above does not use bind parameters, so it's vulnerable to SQL injection .上面的 JPQL 查询不使用绑定参数,因此容易受到SQL 注入的攻击。

Check out what happens when I execute this JPQL query like this:看看当我像这样执行这个 JPQL 查询时会发生什么:

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

Hibernate executes the following SQL query: 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:[()]

Dynamic queries动态查询

You should avoid queries that use String concatenation to build the query dynamically:您应该避免使用字符串连接动态构建查询的查询:

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

If you want to use dynamic queries, you need to use Criteria API instead:如果要使用动态查询,则需要改用 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();

I would like to add here that is a peculiar SQL Injection that is possible with the use of Like queries in searches.我想在这里添加一个特殊的 SQL 注入,可以在搜索中使用 Like 查询。

Let us say we have a query string as follows:假设我们有一个查询字符串,如下所示:

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

While setting the name parameter, most would generally use this.在设置 name 参数时,大多数人通常会使用它。

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

Now, as mentioned above traditional parameter like " 1=1 " cannot be injected because of the TypedQuery and Hibernate will handle it by default.现在,如上所述,像“ 1=1 ”这样的传统参数不能被注入,因为 TypedQuery 和 Hibernate 将默认处理它。

But there is peculiar SQL Injection possible here which is because of the LIKE Query Structure which is the use of underscores但是这里可能存在特殊的 SQL 注入,这是因为使用下划线的 LIKE 查询结构

The underscore wildcard is used to match exactly one character in MySQL meaning, for example, select * from users where user like 'abc_de';下划线通配符用于匹配 MySQL 中的一个字符的含义,例如 select * from users where user like 'abc_de'; This will produce outputs as users that start with abc, end with de and have exactly 1 character in between.这将产生以 abc 开头、以 de 结尾且中间正好有 1 个字符的用户形式的输出。

Now, if in our scenario, if we set现在,如果在我们的场景中,如果我们设置

  • name="_" produces customers whose name is at least 1 letter name="_"生成姓名至少为 1 个字母的客户
  • name="__" produces customers whose name is at least 2 letters name="__"生成姓名至少为 2 个字母的客户
  • name="___" produces customers whose name is at least 3 letters name="___"产生名字至少为 3 个字母的客户

and so on.等等。

Ideal fix:理想的修复:

To mitigate this, we need to escape all underscores with a prefix .为了缓解这种情况,我们需要使用前缀转义所有下划线。

___ will become \\_\\_\\_ (equivalent to 3 raw underscores) ___ 将变成 \\_\\_\\_(相当于 3 个原始下划线)

Likewise, the vice-versa query will also result in an injection in which %'s need to be escaped.同样,反之亦然的查询也将导致需要转义 % 的注入。

We should always try to use stored Procedures in general to prevent SQLInjection.. If stored procedures are not possible;我们应该总是尽量使用一般的存储过程来防止 SQLInjection.. 如果存储过程是不可能的; we should try for Prepared Statements.我们应该尝试准备好的语句。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM