본문 바로가기
Backend2026년 7월 8일4분 읽기

SQL 인젝션, 아직도 1위 취약점 — 원리와 막는 법

YS
김영삼
조회 607
SQL 인젝션, 아직도 1위 취약점 — 원리와 막는 법

핵심 요약

SQL 인젝션은 사용자 입력을 문자열로 이어 붙여 쿼리를 만들 때 발생한다. 입력값이 데이터가 아니라 SQL 문법으로 해석되어 인증 우회·데이터 유출·삭제까지 가능하다. 방어의 정답은 단 하나 — 파라미터 바인딩(prepared statement)으로 데이터와 코드를 분리하는 것이다.

1. 어떻게 뚫리나

// ❌ 문자열 조립
const q = "SELECT * FROM users WHERE id = '" + input + "'"

// 공격자가 input에:  ' OR '1'='1
// 완성된 쿼리:
SELECT * FROM users WHERE id = '' OR '1'='1'
// → 모든 사용자 반환. 인증 우회
// input:  '; DROP TABLE users; --
// → 테이블 삭제까지

2. 방어 — 파라미터 바인딩

// ✅ 값을 자리표시자로 분리 (Node/pg)
db.query('SELECT * FROM users WHERE id = $1', [input])

// ✅ Python
cur.execute('SELECT * FROM users WHERE id = %s', (input,))

DB 드라이버가 input을 항상 데이터로만 취급하므로 SQL 문법으로 해석될 여지가 없다. 문자열을 직접 이어 붙이지만 않으면 된다.

3. ORM도 안전하지만 예외가 있다

  • Prisma·TypeORM 등 ORM의 표준 메서드는 자동으로 바인딩한다
  • 단, $queryRawUnsafe·raw 쿼리에 문자열을 조립하면 똑같이 뚫린다
  • raw가 필요하면 반드시 파라미터 바인딩 방식(태그드 템플릿 $queryRaw)을 쓴다

4. 추가 방어(심층 방어)

계층조치
입력타입·형식 검증(숫자 자리에 문자 거부)
권한앱 DB 계정 최소 권한(DROP 불가 등)
노출에러 메시지에 쿼리·스택 노출 금지
탐지WAF·비정상 쿼리 모니터링

5. 함정

  • 입력 이스케이프를 수동으로 하려 들지 말 것 — 빠뜨리기 쉽다. 바인딩이 정석
  • 테이블·컬럼명은 바인딩이 안 된다 — 화이트리스트로만 허용
  • ORM 쓴다고 방심 금지 — raw 쿼리 조립은 여전히 위험

자주 묻는 질문

ORM을 쓰면 SQL 인젝션은 신경 안 써도 되나요?

표준 쿼리 메서드는 자동으로 파라미터 바인딩을 하므로 안전합니다. 하지만 raw 쿼리에 변수를 문자열로 이어 붙이면 ORM이라도 그대로 뚫립니다. raw를 쓸 땐 반드시 파라미터 방식을 사용하세요.

입력값을 이스케이프하면 안 되나요?

수동 이스케이프는 한 곳만 빠뜨려도 뚫리고 DB·인코딩별로 규칙이 달라 위험합니다. 파라미터 바인딩은 드라이버가 값을 데이터로만 처리하도록 보장하므로 훨씬 안전하고 간단합니다.

정렬 컬럼처럼 컬럼명을 동적으로 받아야 하면요?

컬럼·테이블명은 바인딩이 불가능합니다. 허용할 컬럼 목록을 코드에 화이트리스트로 두고, 입력이 그 목록에 있을 때만 사용하세요. 입력을 그대로 쿼리에 넣으면 인젝션 경로가 됩니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록