FlawOpen

flawopen.com/SQL Injection/이스케이프만으로 충분할까?

따옴표 이스케이프가 SQL Injection을 막을까?

CWE-89: SQL InjectionReference page
요약

대부분 그렇지 않습니다. 이스케이프 함수는 데이터베이스, 연결 문자 집합, SQL 모드, 따옴표 문맥에 따라 달라집니다. 매개변수화 쿼리는 드라이버 API를 통해 값을 SQL 구문과 분리합니다. 값은 매개변수로 전달하고, 바인딩할 수 없는 동적 식별자는 애플리케이션의 고정 허용 목록에서 선택하세요.

실무에서 이스케이프가 취약한 이유

연결 문자 집합은 이스케이프에 영향을 준다

이스케이프는 문맥과 무관한 문자열 치환이 아닙니다. MySQL 문서는 mysql_real_escape_string()이 연결 문자 집합을 사용하며 SET NAMES는 이 함수가 참조하는 클라이언트 라이브러리 상태를 갱신하지 않는다고 설명합니다. 설정이 다르면 이스케이프 문자열에 대한 가정이 틀릴 수 있습니다. 매개변수 API를 우선하고, 기존 코드에서 이스케이프가 필요하다면 지원되는 드라이버 함수로 문자 집합을 설정하고 오류를 처리하세요.

한 번이라도 호출을 빠뜨리면 방어가 약해진다

모든 삽입 지점에서 올바른 드라이버, 문자 집합, SQL 모드, 문자열 리터럴 문맥을 고려해 이스케이프해야 합니다. 매개변수 API는 쿼리 호출에서 값과 데이터를 명시적으로 분리해 따옴표 문자열을 직접 조립하지 않게 합니다.

따옴표 이스케이프는 모든 SQL 문맥을 보호하지 않는다

따옴표 이스케이프는 특정 문자열 리터럴 내부의 문자만 처리합니다. 테이블명, 컬럼명, 키워드, 정렬 방향을 안전하게 만들지 못하고 따옴표 없는 숫자식의 입력도 막지 못합니다. 값은 바인딩하고 동적 SQL 구조는 애플리케이션의 고정 선택지에 매핑하세요.

VULNERABLE
// 사용자 입력이 SQL 구문에 삽입됨
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
HARDENED
// 드라이버가 name을 데이터 값으로 바인딩
cursor.execute(
    "SELECT id FROM users WHERE name = ?",
    (name,),
)

매개변수 바인딩이 바꾸는 점

매개변수화 쿼리는 데이터베이스 API를 통해 SQL 구조와 값을 별도 입력으로 전달합니다. 드라이버는 네이티브 준비된 문장이나 다른 안전한 구현을 사용할 수 있으므로 특정 프로토콜 순서를 전제하지 마세요. 핵심은 값이 데이터로 처리되어 SQL 구문으로 다시 해석되지 않는 것입니다. Python DB-API에서는 드라이버의 자리표시자를 사용하고 값을 별도로 전달하며 문자열 포맷으로 쿼리를 만들지 마세요.

엔지니어링 체크리스트

자주 묻는 질문

이스케이프가 적절한 도구일 때도 있나요?

바인딩할 수 없는 식별자나 SQL 조각에는 드라이버가 지원하는 인용 API와 애플리케이션의 고정 허용 목록을 함께 사용하세요. 임의의 값에 일반 이스케이프를 적용하지 마세요.

드라이버의 이스케이프 함수가 관리되고 있다면 안전한가요?

문서화된 문맥에서는 도움이 될 수 있지만 데이터베이스, 연결 설정, 인용 문맥, 모든 호출 지점의 올바른 사용에 여전히 의존합니다. 값에는 매개변수화 쿼리가 더 안전한 기본값입니다.

참고 자료

함께 보기 SQL 인젝션(CWE-89): 동작 원리와 언어별 방어 방법 — flawopen.com ORM만 쓰면 SQL 인젝션을 막기에 충분한가 Knex.js는 SQL Injection에 안전한가요? MyBatis ${}와 #{}: SQL Injection 방지 MySQLi와 PDO: 안전한 Prepared Statement NHibernate는 SQL Injection을 막을 수 있나요? MongoDB NoSQL Injection: 안전한 쿼리 필터