SQL 인젝션

외부 입력을 SQL 문법과 분리하는 매개변수 바인딩

설명

SQL 인젝션은 외부 입력이 데이터가 아니라 SQL 문법의 일부로 해석될 때 발생합니다. 공격자는 쿼리의 조건이나 동작을 바꿀 수 있으며, 영향은 데이터베이스 계정의 권한과 사용 가능한 기능에 따라 달라집니다.

잠재적 영향

  • 무단 코드 실행: 데이터베이스 기능과 계정 권한이 허용하는 환경에서는 시스템 명령 실행으로 이어질 수 있습니다.
  • 애플리케이션 데이터 읽기: SQL 데이터베이스에 저장된 민감한 정보를 읽을 수 있습니다.
  • 권한 상승: 다른 사용자의 신분으로 시스템에 접근할 수 있습니다.
  • 보호 메커니즘 우회: 인증 및 접근 제어를 우회할 수 있습니다.
  • 데이터 수정: 데이터베이스의 정보를 읽거나 수정, 삭제할 수 있습니다.

해결 방법

  1. PreparedStatement나 JdbcTemplate의 매개변수 바인딩으로 값과 SQL 문법을 분리하세요. 입력을 연결한 뒤 준비하는 것만으로는 충분하지 않습니다.
  2. ORM에서도 문자열로 조합하는 SQL·JPQL·HQL은 같은 위험이 있습니다. 지원되는 바인딩 API를 사용하세요.
  3. 테이블명·열명·정렬 방향처럼 값 매개변수로 표현할 수 없는 요소는 서버가 관리하는 허용 목록에서 선택하세요.
  4. 입력 형식과 길이를 검증하고 데이터베이스 계정에 필요한 최소 권한만 부여하세요.

예시

다음은 JdbcTemplate으로 사용자 레코드를 조회하는 발췌이며 로그인 구현이 아닙니다. 비밀번호 검증과 조회 권한 확인은 별도로 수행하세요.

변경 전

java
String query = "SELECT id, username FROM users WHERE username = '" + username + "'";
jdbcTemplate.queryForList(query);

변경 후

java
String query = "SELECT id, username FROM users WHERE username = ?";
jdbcTemplate.queryForList(query, username);

변경 전에는 username을 쿼리 문자열에 연결합니다. 변경 후에는 조회용 queryForList()에 값을 별도로 전달하므로 username이 SQL 문법으로 해석되지 않습니다.

관련 CVE

참조