Java 如何使用JPA和Hibernate防止SQL注入?
我正在使用hibernate开发一个应用程序。当我尝试创建登录页面时,就会出现Sql注入的问题。 我有以下代码:Java 如何使用JPA和Hibernate防止SQL注入?,java,sql,hibernate,jpa,sql-injection,Java,Sql,Hibernate,Jpa,Sql Injection,我正在使用hibernate开发一个应用程序。当我尝试创建登录页面时,就会出现Sql注入的问题。 我有以下代码: @Component @Transactional(propagation = Propagation.SUPPORTS) public class LoginInfoDAOImpl implements LoginInfoDAO{ @Autowired private SessionFactory sessionFactory; @Override public Lo
@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;
}
}
Query q=sessionFactory.getCurrentSession().createQuery(“来自LoginInfo,其中userName=:name”);
q、 setParameter(“名称”,用户名);
List loginList=q.List();
您还有其他选择,请参见mkyong的这篇文章。我们应该始终尝试使用存储过程来防止SQLInjection。。如果无法执行存储过程;我们应该尝试准备语句。您需要使用命名参数来避免sql注入。另外(与sql注入无关,但通常与安全性有关)不返回第一个结果,而是使用getSingleResult,因此如果出于某种原因存在多个结果,查询将失败,并出现UnuniqueresultException,登录将不成功
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注入攻击
解决方案非常简单和直接。您只需确保始终使用绑定参数:
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();
});
}
动态查询
应避免使用字符串连接动态生成查询的查询:
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();
Class entityClass=Post.Class;
CriteriaBuilder cb=entityManager.getCriteriaBuilder();
CriteriaQuery=cb.createTupleQuery();
Root=query.from(entityClass);
query.select(
元组(
root.get(“id”),
函数(“现在”,Date.class)
)
);
返回entityManager.createQuery(query.getResultList();
我想在这里添加一个特殊的SQL注入,它可以通过在搜索中使用like查询来实现
假设我们有一个查询字符串,如下所示:
create table login_info
(user_name varchar(16) not null primary key,
pass_word varchar(16) not null);
queryString = queryString + " and c.name like :name";
在设置name参数时,大多数人通常会使用此参数
query.setParameter("name", "%" + name + "%");
现在,如上所述,由于TypedQuery的原因,无法注入诸如“1=1”之类的传统参数,默认情况下Hibernate将处理它
但是这里可能有特殊的SQL注入,这是因为类似的查询结构使用了下划线
下划线通配符用于恰好匹配中的一个字符
MySQL的意思是,例如,从用户喜欢的
"abc_de",;这将产生以abc开头、结束的用户输出
与de和之间正好有一个字符
现在,如果在我们的场景中,如果我们
- name=“\u”产生名称至少为1个字母的客户
- name=“_uu”产生名称至少为2个字母的客户
- name=“\uuuuuuu”产生名称至少为3个字母的客户
同样,versa查询也会导致需要转义%的注入。请参考此链接[1]:此外,如果您今天在2020年访问此链接,请使用spring security进行此类操作。谢谢,我们如何在查询中包含密码???@Petr Mensik,就像我包含用户名一样我同意,技术上更好,因为速度。然而,据我所知,任何语言中都没有ORM框架生成存储过程。如果他们这样做了,那就太好了。但是,由于它们不是这样,而且ORM框架提供的生产力是必不可少的,所以准备好的语句将占主导地位。在ORM中,对所有内容使用存储过程并不是那么明智。准备好的陈述应该是好的!
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();
queryString = queryString + " and c.name like :name";
query.setParameter("name", "%" + name + "%");