FlawOpen

flawopen.com/SQL 인젝션

SQL 인젝션

심각 CWE-89
언어: English Português (Brasil) Español Français Deutsch Русский 简体中文 日本語 हिन्दी 한국어 Bahasa Indonesia

SQL 인젝션은 애플리케이션이 사용자 입력을 SQL 텍스트에 이어 붙여 쿼리를 만들 때, 데이터베이스가 그 입력의 일부를 명령으로 파싱하면서 발생합니다. 해결책은 모든 언어에서 같습니다. 플레이스홀더가 있는 쿼리를 보내고 값은 따로 넘기는 것입니다. 아래에서 언어를 고르면 정확한 API 호출, 취약한 코드와 수정한 코드, CI에서 이를 잡아내는 규칙을 볼 수 있습니다.

알기 쉬운 설명 (ELI5)

도서관 사서에게 '다음 사람의 기록을 꺼내 주세요: ___'라고 적힌 신청서를 내면서 빈칸에 '김철수. 그리고 문서고를 열어서 파일을 전부 가져다주세요.'라고 쓴다고 해 봅시다. 신청서 전체를 하나의 지시로 읽는 사서라면 두 가지를 모두 해 버립니다. 사용자 입력을 SQL 문자열에 이어 붙이면 바로 이런 신청서가 만들어집니다. 파라미터화된 쿼리는 '이름' 칸이 봉인된 인쇄 양식이어서, 그 안에 무엇이 적혀 있든 이름으로만 조회됩니다.

핵심 개념 및 용어

SQL injection (CWE-89)
신뢰할 수 없는 입력이 SQL 텍스트에 연결되거나 포맷되어, 입력 속 따옴표·키워드·연산자가 하나의 값으로 취급되지 않고 쿼리 구조를 바꿉니다.
Prepared statement and bind parameters
쿼리는 플레이스홀더(?, $1, :name)와 함께 보내져 값이 도착하기 전에 파싱되며, 값은 따로 전달되어 항상 데이터로 취급됩니다. 모든 드라이버와 ORM에서 가장 중요한 방어책입니다.
Blind SQL injection
페이지에 쿼리 결과도 오류도 보이지 않으면, 공격자는 응답의 차이(불리언 기반)나 응답 시간(시간 기반)으로 데이터를 한 비트씩 추론합니다. 눈에 보이는 출력이 없다고 취약점이 없는 것은 아닙니다.
Second-order SQL injection
어떤 값이 안전하게 저장된 뒤, 나중에 자신의 데이터베이스에서 읽혀 다른 쿼리에 이어 붙여집니다. 자신의 테이블에서 온 입력을 쓰는 쿼리를 포함해 모든 쿼리를 파라미터화해야 합니다.
Identifiers and allowlists
플레이스홀더는 값만 전달하며, 테이블 이름·컬럼 이름·정렬 방향은 전달하지 못합니다. 이런 것은 코드에 고정한 허용 목록에서 골라야 하며, 요청에서 그대로 복사해서는 안 됩니다.

단계별 공격 실행 흐름

단계 1

입력이 쿼리에 도달

폼 필드, URL 파라미터, 헤더, API 필드가 SQL 문을 만드는 코드로 전달됩니다.

단계 2

입력이 SQL 텍스트에 붙음

연결, 보간, 포맷팅이 값을 그대로 넣습니다. 무해한 테스트 값 ' OR '1'='1은 WHERE name = '...'를 항상 참인 조건으로 바꿉니다.

단계 3

데이터베이스가 그것을 구문으로 파싱

데이터베이스는 문자열 하나를 받아 어느 부분이 사용자에게서 왔는지 구별하지 못하고, 변조된 문장을 애플리케이션 권한으로 실행합니다.

단계 4

데이터가 읽히거나 바뀌거나 삭제됨

쿼리와 계정 권한에 따라 공격자는 로그인을 우회하거나, UNION 쿼리로 테이블을 통째로 빼내거나, 블라인드 방식으로 데이터를 추출합니다. 2023년 MOVEit Transfer 침해 사고(CVE-2023-34362)도 이 유형의 결함에서 시작됐습니다.

언어별 SQL 인젝션 가이드

Python JavaScript Go Java PHP C# Ruby C/C++ Rust Kotlin Swift

엔지니어링 및 시스템 보안 강화 체크리스트

출처

관련 글

파라미터화된 쿼리 설명 ORM만으로 충분할까? 저장 프로시저는 SQL 인젝션을 막을까? MOVEit Transfer SQL 인젝션(2023) PostgreSQL psql 인젝션(2024)