Как предотвратить SQL-инъекцию с помощью JPA и 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 в этом сценарии? Синтаксис создания таблицы loginInfo выглядит следующим образом:

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

Ответы

Ответ 1

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

У вас также есть варианты, см. эту приятную статью из mkyong.

Ответ 2

Вам нужно использовать именованные параметры, чтобы избежать внедрения 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();

Ответ 3

Что такое 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 Injection будет предотвращена:

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-инъекция также может происходить при использовании запросов JPQL или HQL, как показано в следующем примере:

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

Для более подробной информации, проверьте эту статью.

Ответ 4

Мы всегда должны пытаться использовать хранимые процедуры в целом для предотвращения SQLInjection. Если хранимые процедуры невозможны; мы должны попробовать подготовленные заявления.

Ответ 5

  • Позиционный параметр в HQL

    Запрос hqlQuery = session.createQuery( "from Orders as orders where orders.id =?" );

    Список результатов = hqlQuery.setString(0, "123-ADB-567-QTWYTFDL" ). list();

  • Именованный параметр в HQL

    Запрос hqlQuery = session.createQuery( "от сотрудников как emp, где emp.incentive > : стимул" );

    Список результатов = hqlQuery.setLong( "стимул", новый длинный (10000)). list();

  • список названных параметров в HQL

    Элементы списка = новый ArrayList(); items.Add( "книга" ); items.Add( "часы" ); items.Add( "чернила" );

    Список результатов = session.createQuery( "из корзины, где cart.item in (: itemList)" ). setParameterList ( "itemList", items).list();

  • JavaBean в HQL

Query hqlQuery = session.createQuery( "из книг в виде книг, где book.name =: name и book.author =: author" );

Список результатов = hqlQuery.setProperties(javaBean).list();

  1. Native-SQL

Query sqlQuery = session.createSQLQuery( "Выбрать * из книг, где author =?" );

Список результатов = sqlQuery.setString(0, "Charles Dickens" ). list();