데이터베이스 쿼리는 injection 공격에 취약해서는 안됩니다.

 

유저가 제공하는 URL 파라미터와 같은 데이터는, 항상 신뢰할수 없고, 손상된 데이터로 간주해야 합니다.
손상된 데이터를 통해 SQL 쿼리를 생성하는 것은, 쿼리의 초기 의미와는 다르게 공격자들이 특별하게 조작된 값을 삽입할 수 있게 합니다.
데이터베이스 쿼리 인젝션 공격이 성공하면 데이터베이스의 민감한 정보들을 읽고, 수정하고, 삭제할 수 있으며 심지어는 데이터베이스를 종료하거나, 임의의 OS 커맨드를 실행할 수 있습니다.

일반적으로, 해결책은 prepared statements 를 사용하고, SQL 쿼리 파라미터를 setString 과 같은 전용 메소드를 사용해 사용자가 제공한 데이터를 적절하게 처리한 채로 바인딩해야합니다.
다른 방법은 모든 파라미터를 쿼리를 생성할 때 검증하는 것입니다.
이것은 String 값은 원시 타입으로 변경하거나, 사용 가능한 값을 white list 로 관리하여 검증하는 것으로 달성할 수 있습니다.

이 룰은 JDBC, Java EE Entity Manager, Spring Framework, Hibernate, JDO, Android Database, Apache Torque, Apache Turbine, MyBastis, Rapidoid 를 지원합니다.

규칙을 어긴 코드

public boolean authenticate(javax.servlet.http.HttpServletRequest request, java.sql.Connection connection) throws SQLException {
    String user = request.getParameter("user");
    String pass = request.getParameter("pass");

    String query = "SELECT * FROM users WHERE user = '" + user + "' AND pass = '" + pass + "'"; // Unsafe

    // 만약 특별한 값인 "foo' OR 1=1 --" 이 user = 혹은 pass = 의 인자로 전달된다면, 인증은 바이패스됩니다.
    // 실제로, 이 값이 유저로 전달된다면 쿼리는 다음과 같습니다.
    // SELECT * FROM users WHERE user = 'foo' OR 1=1 --' AND pass = '...'
    // '--' 는 SQL 의 끝 라인까지 주석이므로 쿼리는 아래와 동일합니다.
    // SELECT * FROM users WHERE user = 'foo' OR 1=1
    // 해당 쿼리는 아래와 동일하게 되고,
    // SELECT * FROM users WHERE 1=1
    // 이것은 아래의 쿼리와 같습니다.
    // SELECT * FROM users

    java.sql.Statement statement = connection.createStatement();
    java.sql.ResultSet resultSet = statement.executeQuery(query); // 규칙을 준수하지 않은 코드
    return resultSet.next();
}

규칙을 준수한 해결책

public boolean authenticate(javax.servlet.http.HttpServletRequest request, java.sql.Connection connection) throws SQLException {
    String user = request.getParameter("user");
    String pass = request.getParameter("pass");

    String query = "SELECT * FROM users WHERE user = ? AND pass = ?"; // authenticate() 메소드가 이러한 상황에 여전히 무차별 공격에 취약하더라도, 안전합니다.

    java.sql.PreparedStatement statement = connection.prepareStatement(query);
    statement.setString(1, user); // 적절하게 escaped 됩니다.
    statement.setString(2, pass);
    java.sql.ResultSet resultSet = statement.executeQuery();
    return resultSet.next();
}

참고


If you like SONARKUBE, don’t forget to give me a star. :star2:

원문으로 바로가기

Star This Project