SQL 인젝션(SQL Injection)

SQL Injection

설명

SQL 인젝션은 사용자 입력값이 그대로 SQL 쿼리 문자열에 연결(concatenation)되거나 포맷팅되어 실행될 때 발생하는 취약점입니다. 개발자가 "SELECT ... WHERE id = " + userInput 과 같이 직접 쿼리를 문자열로 조립하면, 공격자는 1 OR 1=1, 1; DROP TABLE users-- 같은 값을 넣어 쿼리 구조를 조작할 수 있습니다. 이로 인해 인증 우회, 데이터 조회/수정/삭제, 테이블 삭제 등 심각한 공격이 가능해집니다.

잠재적 영향

  • 데이터 조회 및 유출 (정보 노출): 공격자가 원래 볼 수 없는 다른 사용자의 개인정보, 계정 정보, 내부 설정값 등을 조회할 수 있습니다.

  • 데이터 변조 및 삭제 (데이터 무결성 훼손): INSERT, UPDATE, DELETE, DROP 등의 명령을 악용해 데이터 조작, 대량 삭제, 테이블/스키마 삭제가 가능합니다.

  • 인증/인가 우회 (권한 우회): OR 1=1 등의 조건을 주입하여 로그인 우회, 권한 체크 우회가 발생할 수 있습니다.

  • 서비스 장애 (서비스 거부): 대량 쿼리, 무한 루프성 쿼리, 대규모 삭제 등을 실행해 DB와 서비스 전체에 장애를 유발할 수 있습니다.

해결 방법

  • Prepared Statement 사용: db.Query, db.QueryRow, db.Exec 사용 시 문자열 더하기 대신 ?, $1 등의 placeholder 를 사용하고 인자를 별도 파라미터로 전달합니다.

  • ORM 또는 Query Builder 사용: GORM 등 검증된 ORM/Query Builder 를 사용해 직접 문자열로 SQL을 조립하는 일을 최소화합니다.

  • 입력값 타입 검증: ID, 숫자 등은 strconv.Atoi 같은 함수로 숫자로 변환하고, 변환 실패 시 에러 처리하여 문자열 그대로 쿼리에 넣지 않습니다.

  • 화이트리스트 적용: 컬럼명, 정렬 기준(order by) 등 동적 SQL이 꼭 필요하다면, 미리 정의된 값 집합(화이트리스트)에서만 선택 가능하게 합니다.

  • 최소 권한 원칙: DB 계정에 불필요한 권한(예: DROP, ALTER 등)을 주지 않아, 인젝션 발생 시 피해를 줄입니다.

  • 에러 메시지 관리: DB 에러를 그대로 사용자에게 노출하지 말고, 내부 로그에만 상세 정보를 남기고 사용자에게는 일반적인 메시지만 보여줍니다.

취약한 코드 및 안전한 코드 예시

취약한 코드

안전한 코드

설명:

  • 취약한 코드: unsafeUserDetailHandler 함수는 HTTP 요청에서 받은 id 값을 아무 검증 없이 문자열로 사용해서 "... WHERE id = " + userID 형태로 SQL을 직접 조립합니다. 이 경우 공격자가 /user?id=1 OR 1=1 -- 와 같이 입력하면 실제 실행되는 쿼리는 SELECT ... WHERE id = 1 OR 1=1 -- 가 되어, 모든 사용자 정보가 조회될 수 있습니다. 더 나아가 1; DROP TABLE users-- 같은 입력으로 데이터베이스 구조를 파괴하는 공격도 가능합니다.

  • 안전한 코드: safeUserDetailHandler 함수는 먼저 strconv.Atoiid 값을 정수로 변환해, 숫자가 아닌 값은 바로 차단합니다. 이후 SQL 문은 고정된 문자열("SELECT ... WHERE id = ?")로 두고, userID 값은 db.QueryRow(query, userID) 의 인자로 별도 전달합니다. 이렇게 Prepared Statement/placeholder 를 사용하면, 입력값은 단순한 데이터로만 처리되고 SQL 문법으로 해석되지 않아, 공격자가 어떤 문자열을 넣어도 쿼리 구조를 바꾸는 SQL 인젝션이 불가능해집니다.

참조

Last updated