SQL 인젝션은 검색창에 입력한 텍스트가 데이터베이스에 대한 "명령의 일부"가 되는 취약점입니다. 동작 원리와 확실하게 막는 방법을 설명합니다(공격 절차 없음).
왜 일어나는가 (메커니즘)
근본 원인은 문자열 이어 붙이기로 SQL을 만드는 것입니다. 입력을 문장에 곧장 섞으면 "값의 경계"를 넘어 명령으로 작동할 수 있습니다.
✗ 문자열 이어 붙이기 (위험)
입력이 문장에 이어 붙여짐 → 입력이 명령의 일부로 읽힐 수 있음
✓ 플레이스홀더 (안전)
값을 ? / $1 자리에 데이터로 넘김 → 항상 값으로 남음
피해는 "그 DB 연결이 할 수 있는 모든 것"입니다. 내부 DB에 닿을 수 있다면, RCE 와 SSRF 와 나란히 대규모 유출의 고전적 원인입니다.
방어
플레이스홀더 / 프리페어드 스테이트먼트 사용 (가장 중요)
이어 붙이기로 SQL을 만들지 마세요. 값을 ? / 이름 있는 파라미터로 '데이터'로 넘기세요. 이것만으로 이 부류 전체가 거의 사라집니다. 핵심은 입력에서 따옴표를 제거하는 것이 아니라, 값과 명령을 서로 다른 채널로 분리해 데이터베이스가 그 둘을 결코 헷갈리지 않게 하는 것입니다.
ORM / 쿼리 빌더에 기대기
대부분의 ORM은 기본으로 플레이스홀더를 씁니다. 직접 작성하는 날 SQL이 적을수록 안전합니다. 날 SQL을 꼭 써야 한다면 반드시 파라미터화하세요.
최소 권한 DB 사용자
앱의 DB 사용자에게 필요 없는 권한(DROP, 다른 테이블, 관리 작업)을 주지 마세요. 뚫리더라도 피해를 봉쇄합니다. 읽기만 하는 화면이라면 읽기 전용 계정으로 연결해, 만약의 경우에도 데이터가 변경·삭제되지 않게 하세요.
보조 수단으로서의 입력 검증
타입/길이/형식 검사는 도움이 되지만, 주된 방어로 삼지 마세요. 플레이스홀더를 보완하는 것입니다.
본 사이트의 견해: 애초에 날 SQL을 손으로 짓기를 멈춰라
SQLi는 아주 오래전부터 알려져 있었지만 여전히 일어납니다. 입력을 문장에 섞는 것이 "편하기" 때문입니다. 본 사이트의 입장은 명확합니다. 값이 SQL에 문자열로 이어 붙여지는 자리를 절대 만들지 마라. 플레이스홀더나 ORM을 기본으로 삼으면 이 취약점은 설계상 막을 수 있습니다. 수동 이스케이프에 의존하지 마세요.
주의: 플레이스홀더는 값만 나른다
플레이스홀더가 모든 것을 해결하지는 않습니다. 값만 나르며, 테이블명, 컬럼명, ORDER BY 방향(오름차순/내림차순) 같은 쿼리 구조는 나르지 않습니다.
그것들이 동적으로 필요할 때(예: 사용자가 "정렬 키"나 "필터 컬럼"을 고르게 할 때)는 입력을 SQL에 떨어뜨리지 말고, 미리 정의된 허용 목록에서 실제 이름을 고르세요. "정렬 컬럼"과 "필터 컬럼"은 사람들이 플레이스홀더가 알아서 처리해 준다고 착각하는 고전적 맹점입니다.
입력은 폼 필드만이 아니다
검색창과 로그인 폼만 조심하면 된다고 생각하면 놓치는 곳이 생깁니다. SQL에 들어갈 수 있는 값은 여러 경로로 들어옵니다.
- HTTP 헤더(
User-Agent,X-Forwarded-For등)와 쿠키 - API가 받은 JSON의 값, 파일에서 읽은 값
- 한 번 데이터베이스에 저장된 뒤 다른 처리가 나중에 SQL로 조립하는 값
마지막 것은 2차(second-order) SQL 인젝션이라고 합니다. 저장할 때 플레이스홀더를 썼더라도, 다른 처리가 나중에 저장된 값을 문자열로 이어 붙이면 통합니다. "내 데이터베이스에서 왔으니 안전하다"고 여기지 말고, SQL을 조립하는 모든 곳에서 플레이스홀더를 쓰세요.
본 사이트의 X-Forwarded-For 위장 해설에서는 탐색용 문자열이 헤더로 들어왔지만, 값을 플레이스홀더로 넘겼기 때문에 SQL 인젝션이 통하지 않은 사례를 소개합니다.
비슷한 용어와의 차이
이름에 "인젝션"이 들어간 결함에는 공통점이 있습니다. 입력이 다른 언어의 명령으로 읽힌다는 것입니다. 차이는 입력이 어디로 가느냐입니다.
| 용어 | 입력이 가는 곳 | 주된 피해 | 근본 해결책 |
|---|---|---|---|
| SQL 인젝션 | 데이터베이스에 대한 명령(SQL) | 데이터 읽기, 변경, 삭제 | 플레이스홀더 |
| XSS | 사용자의 브라우저에서 실행되는 HTML이나 JavaScript | 사용자 세션의 악용, 페이지의 가짜 콘텐츠 | 출력 시 이스케이프와 CSP |
| OS 명령 인젝션 | 서버의 셸이 실행하는 명령 | 서버에서 임의 명령 실행(RCE) | 셸을 거치지 않고 인자를 따로 넘기는 API를 쓴다 |
SQL 인젝션과 XSS는 자주 함께 언급되지만, 피해가 생기는 곳이 다릅니다. SQL 인젝션은 서버의 데이터베이스를, XSS는 사용자의 브라우저를 노립니다.
주요 언어에서 플레이스홀더를 부르는 이름
주요 언어는 모두 값을 따로 넘기는 방법을 제공합니다. 내 스택에서 어떤 기능을 쓸지 고르는 기준으로 삼으세요.
| 언어 / 라이브러리 | 쓸 기능 |
|---|---|
| Java(JDBC) | PreparedStatement와 setString 같은 값 설정 메서드 |
| PHP(PDO) | prepare로 문을 준비하고, 값은 execute에 넘긴다 |
| Python(DB-API) | 값을 execute의 두 번째 인자로 넘긴다(SQL 문자열에 끼워 넣지 않는다) |
| Node.js(pg 등) | $1 같은 번호 기호를 쓰고, 값은 별도 배열로 넘긴다 |
| C#(.NET) | 명령의 Parameters에 값을 추가한다 |
어떤 언어든 템플릿 문자열이나 +로 값을 SQL에 끼워 넣는 순간 이 보호는 작동하지 않습니다.
실제 사례: 제품의 SQL 인젝션에서 시작된 대규모 유출
SQL 인젝션은 오래전부터 알려져 있었지만 지금도 큰 유출을 일으킵니다. 최근 사례는 직접 작성한 코드가 아니라 사용하던 제품에서 시작된 경우가 많습니다.
| 사례 | 일어난 일 |
|---|---|
| MOVEit Transfer(2023) | 파일 전송 제품의 SQL 인젝션(CVE-2023-34362)이 수정판이 나오기 전에 악용되어, 2,700곳 이상의 조직에서 데이터가 빠져나갔고 그중 상당수는 위탁업체를 통한 것이었다(→ MOVEit 대규모 데이터 유출) |
| Metabase(2026) | 분석 도구의 인증 없는 SQL 인젝션(CVE-2026-72898)이 수정 전에 악용되어, 여러 기업이 고객 데이터 유출을 공표했다(→ Metabase 악용과 유출(영문)) |
| WordPress 코어(2026) | 다른 결함과 결합하면 인증 없는 SQL 인젝션이 RCE로 이어질 수 있었다(→ CVE-2026-63030 해설(영문)) |
MOVEit 이후 미국 CISA와 FBI는 2024년 3월 소프트웨어 제조사에 SQL 인젝션을 없애라고 요구하는 경보를 내고, 해결책으로 파라미터화된 쿼리(플레이스홀더)를 들었습니다. 2025년 12월 공개된 MITRE의 "CWE Top 25"에서 SQL 인젝션(CWE-89)은 2위입니다.
입장에 따라 할 일이 다르다
| 입장 | 먼저 할 일 |
|---|---|
| SQL을 작성하는 쪽(개발자) | 문자열 이어 붙이기를 멈추고 플레이스홀더나 ORM을 쓴다. 날 SQL 함수의 사용처를 점검한다 |
| 제품이나 오픈소스를 운영하는 쪽(운영자) | 운영 중인 제품의 CVE와 KEV 등재 여부를 확인하고 수정판으로 업데이트한다. 인터넷에서 닿는 관리 화면을 줄인다 |
| 양쪽 모두 | 앱의 데이터베이스 사용자 권한을 필요한 테이블과 컬럼으로 좁힌다 |
내 환경에서 확인하는 방법
공격 문자열을 입력해 시험할 필요는 없습니다. 코드와 설정으로 필요한 것을 알 수 있습니다.
문자열로 만든 SQL을 찾는다
코드에서 SQL 키워드(SELECT, WHERE 등)가 문자열 연결이나 템플릿 문자열과 함께 나오는 줄을 검색합니다. 날 SQL 함수($queryRawUnsafe, whereRaw, DB::raw, raw() 등)에 입력이 이어 붙여지지 않는지도 확인하세요.
데이터베이스 사용자가 할 수 있는 일을 적어 본다
앱의 데이터베이스 사용자가 어떤 테이블에 접근해 무엇을 할 수 있는지 확인합니다(MySQL은 SHOW GRANTS, PostgreSQL은 information_schema.role_table_grants). 필요 없는 삭제 권한이나 다른 앱의 테이블에 대한 접근은 없애세요.
사용 중인 제품을 취약점 정보와 대조한다
CVE / KEV 조회로 운영 중인 제품의 알려진 취약점과 악용 여부를 확인합니다. 의존성은 osv-scanner로 한꺼번에 검사할 수 있습니다.
SQL 오류가 화면에 나오지 않는지 확인한다
운영 환경에서 데이터베이스 오류 메시지가 그대로 사용자에게 표시되지 않는지 확인하세요. 자세한 오류는 로그에만 남깁니다.
로그에서 징후를 찾는다
데이터베이스 문법 오류가 갑자기 늘지 않았는지, WAF나 액세스 로그에 같은 출처에서 SQL 같은 문자열이 들어간 요청이 연달아 기록되지 않았는지 확인하세요. 그렇다면 그 경로 뒤의 코드부터 점검합니다.
출처(1차 자료)
- OWASP, "SQL Injection Prevention Cheat Sheet" — cheatsheetseries.owasp.org(플레이스홀더, 저장 프로시저 주의점, 허용 목록 기반 입력 검증, 최소 권한, 이스케이프에 의존하지 않기)
- CISA·FBI, "Secure by Design Alert: Eliminating SQL Injection Vulnerabilities in Software"(2024년 3월 25일) — cisa.gov
- MITRE, "2025 CWE Top 25 Most Dangerous Software Weaknesses"(2025년 12월) — cwe.mitre.org(CWE-89가 2위)
- IPA, "안전한 웹사이트 만드는 법"(일본어) — ipa.go.jp(근본적 해결과 보험적 대책)
다음으로 읽기
- 용어집: RCE란 · XSS란 · OWASP Top 10이란
- 해설: X-Forwarded-For 위장과 신뢰할 프록시 설정
- 기초: 보안 기초