SQL 인젝션 (SQL Injection)

한눈에 보기

SQL 인젝션은 입력값에 악의적 SQL을 섞어 의도하지 않은 쿼리를 실행시키는 공격이다. 유니언, 에러, 블라인드 유형과 준비된 문장 기반 방어, ORM에서도 취약해지는 경우까지 다룬다.

최종 업데이트 2026.09.17

SQL 인젝션(SQL Injection)은 사용자 입력값에 악의적인 SQL 구문을 섞어 넣어, 개발자가 의도하지 않은 쿼리를 데이터베이스에서 실행시키는 공격 기법이다. 입력값을 검증 없이 쿼리 문자열에 그대로 이어 붙일 때 발생하며, 인증 우회, 데이터 유출, 데이터 파괴까지 이어질 수 있는 대표적인 웹 취약점이다.

공격 원리

애플리케이션이 사용자 입력을 문자열 연결로 쿼리에 끼워 넣으면, 데이터베이스는 입력값과 SQL 문법을 구분하지 못한다. 로그인 쿼리를 예로 들면 다음과 같다.

-- 개발자가 의도한 쿼리 (입력을 문자열로 이어 붙인 경우)
SELECT * FROM users
WHERE username = '입력한 아이디' AND password = '입력한 비밀번호';

-- 아이디에 admin'-- 을 입력하면 실제 실행되는 쿼리
SELECT * FROM users
WHERE username = 'admin'--' AND password = '아무거나';

주석 기호 뒤의 비밀번호 검사 조건이 통째로 무시되어 비밀번호 없이 관리자로 로그인된다. 마찬가지로 ' OR '1'='1 같은 입력은 조건을 항상 참으로 만들어 전체 행을 반환하게 한다. 핵심은 데이터여야 할 입력이 코드로 해석된다는 점이다.

공격 유형

유니언 기반(Union-Based)

UNION 연산자로 공격자가 원하는 다른 테이블의 조회 결과를 원래 결과에 이어 붙여 화면에 노출시키는 방식이다. 게시글 목록을 보여 주는 쿼리에 UNION SELECT username, password FROM users를 주입해 계정 정보를 빼내는 식이다.

에러 기반(Error-Based)

일부러 오류를 유발해서, 오류 메시지에 포함되어 나오는 데이터베이스 구조나 데이터 조각을 수집하는 방식이다. 상세한 오류 메시지를 사용자에게 그대로 보여 주는 서비스에서 특히 위험하다.

블라인드(Blind)

쿼리 결과가 화면에 직접 나오지 않을 때, 참과 거짓에 따른 응답 차이나 응답 시간 차이로 데이터를 한 글자씩 추론하는 방식이다. 조건이 참일 때만 페이지가 정상 표시되는 불리언 기반(Boolean-Based)과, SLEEP 같은 지연 함수를 이용해 응답 시간으로 판별하는 시간 기반(Time-Based)이 있다. 느리지만 자동화 도구를 쓰면 데이터베이스 전체를 덤프할 수 있다.

가장 확실한 방어: 매개변수화된 쿼리

근본적인 해결책은 SQL 구문과 데이터를 처음부터 분리해서 전달하는 것이다. 준비된 문장(Prepared Statement)을 사용하면 쿼리의 구조가 먼저 컴파일되고 입력값은 바인딩 변수로 나중에 채워지므로, 입력에 어떤 SQL 문법이 들어 있어도 단순한 문자열 값으로만 취급된다.

-- 매개변수화된 쿼리: 물음표 자리에는 값만 바인딩된다
SELECT * FROM users WHERE username = ? AND password = ?;

자바(Java)의 PreparedStatement, JPA와 같은 ORM(Object-Relational Mapping)의 파라미터 바인딩, 마이바티스(MyBatis)의 #{} 표기가 모두 이 방식이다. 반대로 마이바티스의 ${}처럼 값을 문자열로 치환하는 문법은 주입 위험이 그대로 남으므로, 테이블명이나 정렬 컬럼처럼 바인딩이 불가능한 자리에 한해 화이트리스트(Whitelist) 검증과 함께만 써야 한다.

다층 방어 전략

매개변수화가 1차 방어선이라면, 다음 장치들이 피해 범위를 줄이는 추가 방어선이 된다.

  • 입력값 검증: 숫자 파라미터는 숫자 타입으로 파싱하고, 허용 값 목록이 정해진 입력은 화이트리스트로 검증한다.
  • 최소 권한 원칙: 애플리케이션용 데이터베이스 계정에는 필요한 테이블의 필요한 권한만 부여한다. 조회 기능만 있는 서비스 계정이 DROP 권한을 가질 이유가 없다.
  • 오류 메시지 숨기기: 데이터베이스 오류를 사용자에게 그대로 노출하지 않고, 일반적인 오류 페이지로 대체한 뒤 상세 내용은 서버 로그에만 남긴다.
  • 웹 방화벽(WAF): 알려진 공격 패턴을 네트워크 계층에서 차단해 방어를 보강한다. 단 우회 기법이 많으므로 근본 대책은 아니다.
  • 정기 점검: 정적 분석 도구와 침투 테스트로 문자열 연결 쿼리가 남아 있지 않은지 지속적으로 확인한다.

ORM을 써도 안심할 수 없는 경우

ORM을 사용하면 대부분의 쿼리가 자동으로 매개변수화되지만, 네이티브 쿼리(Native Query)를 문자열로 조립하거나 동적 정렬 조건을 입력값으로 직접 이어 붙이면 취약점이 다시 생긴다. 검색 조건, 정렬 기준, LIMIT 값처럼 동적으로 변하는 부분이 문자열 연결로 만들어지고 있지 않은지가 코드 리뷰에서 확인해야 할 핵심 포인트다. 프레임워크가 지켜 주는 범위와 개발자가 직접 지켜야 하는 범위를 구분하는 것이 중요하다.

면접에서 이렇게 물어본다

  • SQL 인젝션이 발생하는 원리를 로그인 쿼리 예시로 설명해 보라.
  • 준비된 문장이 SQL 인젝션을 막을 수 있는 이유는 무엇인가.
  • 블라인드 SQL 인젝션은 결과가 보이지 않는데 어떻게 데이터를 빼내는가.
  • ORM을 사용하는 프로젝트에서도 SQL 인젝션이 발생할 수 있는 경우를 들어 보라.
  • 매개변수화 외에 함께 적용해야 할 방어 수단에는 어떤 것이 있는가.
데이터베이스 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기