Secure Coding Guide | SQL 삽입


정의

검증되지 않은 외부 입력 값이 SQL쿼리문 생성에 사용되어 악의적인 쿼리가 실행될 수 있는 보안약점이다.
웹에플리케이션에 대해서 가해지는 가장 흔한 공격 기법 중의 하나로 에플리케이션의 허점을 이용해서 데이터베이스를 비정상적으로 조작하는 기법이다.

영향

조작된 쿼리를 통해 DB 정보가 노출되거나 변조, 불법 로그인할 수 있다.

안전하지 않은 소스코드 예

안전하지 않은 Java 코드 일부

try {
    String tableName = props.getProperty("jdbc.tableName");
    String name = props.getProperty("jdbc.name");
    String query = "SELECT * FROM " + tableName + " WHERE Name =" + name;
    stmt = con.createStatement(query);
    rs = stmt.executeQuery();
   ...
} catch (SQLException sqle) { ... } finally { ... }

tableName과 name의 값에 대해 대한 검증을 수행하지 않는다.

안전하지 않은 MyBatis xml 코드 일부

<select id="selectByUserId" resultType="com.devkuma.dto.User">
    SELECT user_id,
      FROM devkuma_user
     WHERE user_id = '${userId}'
</select>

user_id = ‘${userId}’ 의 경우 외부에서 SQL 삽입이 가능할 경우 쿼리문의 구조가 변경된다.

잘못된 조치 방법

잘못된 조치방법

String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty("jdbc.name");
String query = "SELECT * FROM " + tableName + " WHERE Name =" + name;
stmt = con.prepareStatement(query);
rs = stmt.executeQuery();
...

PreparedStatement 를 사용하였으나, setString등의 상수 스트링을 이용하여 쿼리문의 구조가 변경 가능하다. 이는 잘못된 PreparedStatement 사용법이다.

안전한 소스코드

String tableName = props.getProperty("jdbc.tableName");
String name = props.getProperty("jdbc.name");
String query = "SELECT * FROM ? WHERE Name = ?";
stmt = con.prepareStatement(query);
stmt.setString(1, tableName); stmt.setString(2, name);
rs = stmt.executeQuery();
...

setString등의 상수 스트링을 이용하여 쿼리문 구조가 변경되지 않는 PreparedStatement클래 스를 이용한다.

<select id="selectByUserId" resultType="com.devkuma.dto.User">
    SELECT user_id,
      FROM devkuma_user
     WHERE user_id = #{userId}
</select>

Mybatis 또는 iBatis의 sql map에 서 외부 입력이 쿼리로 구성되는 경우 ${..} 대신에 쿼리의 구조가 변경되지 않는 #{..}을 이용한다.

참고 자료

SQL삽입 취약점 제거를 위해 입력 필터링이 요구되는 특수문자

'" #-();@ =* /+