SQL 인젝션(SQLi) 공격 정리: 메커니즘부터 유형별 난이도·방어 전략까지

SQL 인젝션(SQLi) 공격 정리: 메커니즘부터 유형별 난이도·방어 전략까지

웹서비스는 거의 항상 DB(RDBMS) 와 커뮤니케이션합니다. 문제는 개발자가 사용자 입력값(User Input) 을 그대로 SQL 문장에 붙여버리면, 그 입력값이 데이터가 아니라 SQL 명령(코드) 로 해석될 수 있다는 점이에요.

그래서 SQL 인젝션은 단순 유출을 넘어 데이터 위·변조, 로그인 우회, 권한 상승, 심지어 서버 장악(환경에 따라) 까지 이어질 수 있습니다. OWASP가 꾸준히 치명적 위험으로 분류하는 이유도 여기 있습니다.

이 글에서는 SQLi를 아래 4개로 나눠서 작동 방식(메커니즘) → 난이도 → 방어법 순으로 정리해 드립니다.

  • [CASE A] 폼(Form) 기반(클래식)
  • [CASE B] 유니온(UNION) 기반
  • [CASE C] 오류(Error) 기반
  • [CASE D] 블라인드(Blind) 기반(불리언/시간)

[본론] 0. 핵심 원리: “데이터 평면 vs 제어 평면”

SQLi를 한 줄로 끝내면 이겁니다.

사용자 입력값(데이터)이 SQL 문법(제어/명령)으로 해석되는 순간 공격이 됩니다.

예를 들어 원래 의도는 “id가 1인 상품만” 조회인데,

  • 원래 의도: ... WHERE id = 1
  • 입력 오염: ... WHERE id = 1 OR 1=1

이렇게 되면 DB는 “조건이 항상 참이니 전부 다 줘”로 이해해버립니다.
즉, 입력값이 값이 아니라 로직으로 기능하게 되는 거죠.


[CASE A] 폼(Form) 기반 SQLi — 로그인 우회/논리 조작 (난이도: 하)

1) 메커니즘

로그인 쿼리의 전형은 아래 형태입니다.

SELECT * FROM users
WHERE username = 'INPUT_USER'
AND password = 'INPUT_PASSWORD';

공격자는 여기서 AND 검증을 깨고, OR로 “무조건 참”을 만들어 인증을 우회합니다(동어반복/Tautology).

2) 왜 성공하나? (초보자용 비유)

은행 창구에 “100만 원 인출” 요청서를 내면서, 옆에 작은 메모로 “그리고 금고 문도 열어” 를 붙여놓는 겁니다.
창구 직원(DB)이 그 메모를 요청 데이터가 아니라 ‘추가 지시’로 읽어버리면 사고가 납니다.

3) 난이도 평가: 하(Low)

  • 브라우저만 있어도 시도 가능
  • 성공/실패 피드백이 즉시 보임(로그인 성공, 결과 변화)
  • SQL 기초 문법(AND/OR/주석) 정도면 공격 이해 가능

[CASE B] 유니온(UNION) 기반 SQLi — “원래 결과 + 내가 원하는 결과” 합치기 (난이도: 중)

1) 메커니즘

UNION은 SELECT 결과를 합치는 연산자입니다. 공격자는 이를 이용해
“상품 목록”을 보여주는 페이지에 users 테이블(계정/비번 등) 을 끼워 넣어 출력시키려고 합니다.

2) 성공 조건

UNION 기반은 아무 때나 되는 게 아니라 조건을 맞춰야 합니다.

  • 컬럼 수가 동일해야 함
  • 컬럼 타입이 호환되어야 함(숫자 자리엔 숫자/호환 타입)

3) 공격 흐름

  • (1) 컬럼 개수 파악
  • (2) 화면에 “어느 컬럼이 출력되는지” 확인
  • (3) 출력 위치에 원하는 데이터 SELECT로 끼워 넣기

4) 난이도 평가: 중

  • 컬럼 수/타입 맞추는 시행착오가 필요
  • 대신 In-band라 데이터 추출 속도는 매우 빠름(화면으로 바로 뜸)

[CASE C] Error 기반 SQLi — 고의적으로 에러 메시지 나오게 하기  (난이도: 중)

1) 메커니즘

DB는 오류가 나면 친절하게 이유를 설명합니다.
운영환경에서 상세 에러를 사용자에게 그대로 보여주면, 공격자는 그 메시지에 원하는 데이터를 점진적으로 빼낼 수 있어요.

핵심은 이거예요.

“내가 원하는 값이 에러 메시지에 찍히도록” 오류를 유도한다.

2) 환경 제약(중요)

요즘 서비스는 보통 “오류가 발생했습니다”만 보여주고, 상세 에러는 숨깁니다.
이 경우 Error-based는 잘 안 먹고, 공격자는 [CASE D] 블라인드로 넘어갑니다.

3) 난이도 평가: 중(Medium)

  • DBMS별로 “어떤 에러가 어떤 메시지를 뱉는지” 지식이 필요
  • 하지만 성공하면 비교적 빠르게 단서/데이터를 확보할 수 있음

[CASE D] 블라인드(Blind) SQLi — 화면에 데이터가 안 떠도, “스무고개”로 뽑아냄 (난이도: 상)

블라인드는 에러도 없고, 결과 출력도 없는 상황에서 데이터를 빼내는 방식입니다.
그래서 가장 까다롭고, 실무에서 더 자주 마주칩니다.

1) 불리언(Boolean) 기반 블라인드

페이지 반응이 “참/거짓”에 따라 미세하게 달라질 때 씁니다.

  • 조건이 참이면 정상 페이지
  • 조건이 거짓이면 빈 페이지/다른 문구

이 차이를 이용해서 “첫 글자가 a냐?” 같은 질문을 계속 던져서 한 글자씩 복원합니다.
효율을 위해 보통 이진 탐색(> ‘m’ 같은 비교) 로 속도를 끌어올립니다.

2) 시간(Time) 기반 블라인드

참/거짓에 따른 화면 차이조차 없으면, 마지막 단서는 응답 시간입니다.

  • 조건이 참이면 5초 지연
  • 조건이 거짓이면 즉시 응답

즉 “맞으면 일부러 천천히 대답해” 전략이에요.

3) 난이도 평가: 상(High)

  • 수작업으로는 거의 불가능(요청이 수백~수천 번)
  • 자동화 도구/스크립트가 사실상 필수
  • 트래픽/로그가 많이 남아 탐지 가능성도 커짐

공격 유형별 비교 요약표

구분난이도추출 속도피드백(단서)한 줄 요약
폼(Form) 기반하빠름화면 변화(로그인/검색)논리 조작으로 바로 우회
UNION 기반중매우 빠름화면에 데이터 직접 출력결과 집합 합쳐서 덤프
Error 기반중보통에러 메시지에러에 정보를 실어 유출
Blind 기반상매우 느림참/거짓 변화 또는 시간 지연스무고개로 한 글자씩

방어 전략: “데이터와 SQL 코드를 분리”하면 끝납니다

SQLi 대응은 철학이 단순합니다.

입력값은 입력값으로만 처리하고, SQL 문법(코드)로 절대 해석되지 못하게 만든다.

1) [1순위] Prepared Statement(매개변수화 쿼리) — 사실상 정답

문장을 먼저 확정(Prepare) 하고, 값은 나중에 바인딩(Bind) 합니다.
그러면 공격자가 ' OR '1'='1을 넣어도 DB는 그걸 명령어가 아니라 문자열 값으로 취급합니다.

취약한 방식(금지)

query = "SELECT * FROM users WHERE username = '" + username + "'"
cursor.execute(query)

안전한 방식(권장)

query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))

실무 팁: “입력값 검증 잘하면 되지 않나?”라고 생각하기 쉬운데, 검증은 보조고 Prepared Statement가 본체입니다.


2) 입력값 검증(Validation) — 화이트리스트 우선

  • 화이트리스트(Allow-list): 허용 가능한 형식만 통과(가장 안전)
    • 예: 나이=숫자만, 아이디=영문+숫자만
  • 블랙리스트(Block-list): ', -- 같은 위험 문자 차단
    • 우회(인코딩/변형)가 많아서 단독 방어로는 부족합니다.

3) 최소 권한 원칙(Least Privilege) — 뚫려도 “피해를 제한”

웹앱 DB 계정에 필요 최소 권한만 부여하세요.

  • 읽기 기능만 있으면 SELECT만
  • DROP/GRANT 같은 관리 권한 금지
  • (환경에 따라) OS 명령 실행 기능도 차단

4) 운영 보안 세팅 — “단서”를 지워라

  • 상세 DB 에러 메시지 사용자에게 미노출(로그로만 확인)
  • WAF는 보조 수단(우회 가능성 있음)
  • 모니터링/레이트리밋/탐지 룰로 Blind 트래픽(다량 요청) 탐지

[결론] 3줄 요약

  1. SQL 인젝션은 입력값이 SQL 코드로 해석되는 순간 발생해요.
  2. 폼/UNION/Error/Blind는 피드백 채널(화면·에러·시간) 차이로 구분됩니다.
  3. 방어는 Prepared Statement + 화이트리스트 검증 + 최소권한 + 에러 숨김이면 실무급으로 막을 수 있어요.