✅ SQL 인젝션(SQL Injection)의 구조와 왜 발생하는지 자세한 설명
SQL 인젝션은 웹 애플리케이션에서 사용자 입력이 SQL 쿼리의 일부로 해석되면서 공격자가 의도하지 않은 SQL 명령을 실행시키는 취약점입니다.
아래 내용을 단계별로 이해하시면 전체 구조가 명확하게 보입니다.
1️⃣ 웹 애플리케이션에서 SQL이 동작하는 기본 구조
일반적인 웹 서비스는 다음과 같은 흐름으로 작동합니다.
-
사용자가 로그인 ID, 검색어 등을 입력함
-
서버(PHP, Python, Node.js 등)가 입력값을 받아 SQL 쿼리를 생성함
-
생성된 SQL 문을 DB 서버(MySQL 등)에 전송
-
DB 서버가 결과를 서버에게 되돌려줌
-
서버는 그 결과를 HTML로 만들어 사용자에게 보여줌
예를 들어 로그인을 한다면 서버는 대략 이런 쿼리를 만들게 됩니다.
여기까지는 정상적인 흐름입니다.
2️⃣ SQL 인젝션이 어디서 끼어드는가? (취약점의 구조)
문제는 서버가 SQL 쿼리를 만들 때 문자열을 이어 붙이는 방식을 사용하면 발생합니다.
예시(취약한 코드):
위처럼 문자열로 직접 이어 붙이면, 사용자의 입력이 “코드”처럼 동작할 수 있습니다.
✔ 예시 — 공격자가 입력한 값
-
username:
admin -
password:
' OR '1'='1
그러면 최종적으로 DB에 전달되는 SQL은 이렇게 됩니다:
여기서 '1'='1' 은 항상 참(True)이므로
비밀번호 검증이 완전히 무너져 버립니다.
즉,
사용자가 입력한 값이 SQL 문법으로 해석되면서 원래 의도를 벗어난 SQL이 실행됨
이 구조가 SQL 인젝션입니다.
3️⃣ 왜 이런 공격이 발생하는가? (근본 원인)
SQL 인젝션의 근본 원인은 다음과 같습니다.
✔ (1) 입력값과 SQL 코드가 구분되어 있지 않기 때문
서버가 입력값을 별도의 데이터로 처리하지 않고
그냥 쿼리 문자열에 직접 끼워 넣어버리기 때문입니다.
DB 입장에서는 이것이 데이터인지, 공격용 SQL인지 구분할 수 없습니다.
✔ (2) 입력값 검증 부족
-
특수문자( ', " , -- , # 등 ) 필터링 실패
-
타입 체크 부족
-
잘못된 escape 처리
이러한 문제들이 쿼리를 무방비하게 만듭니다.
✔ (3) Prepared Statement(준비된 문) 사용 안 함
SQL 인젝션이 없는 안전한 방식은 바로 **Prepared Statement(프리페어드 스테이트먼트)**입니다.
예시:
이 방식은 DB가
“? 부분은 무조건 데이터로만 처리하고, 절대 SQL 코드로 해석하지 않는다”
고 약속하는 방식이기 때문에 SQL 인젝션이 원천적으로 차단됩니다.
4️⃣ SQL 인젝션으로 어떤 일이 벌어질 수 있는가?
SQL 인젝션은 웹 해킹에서 가장 위험한 취약점 중 하나이며, 다음과 같은 공격이 가능합니다.
✔ 로그인 우회
비밀번호 없이 admin으로 로그인
✔ DB 내용 탈취
회원 정보, 비밀번호 해시, 이메일 등 전체 조회 가능
✔ 테이블 삭제
✔ 시스템 명령 실행 (DB의 기능에 따라)
MySQL의 LOAD_FILE, INTO OUTFILE 등 악용 가능
✔ 서버 권한 상승 공격으로 이어질 수도 있음
즉, SQL 인젝션은 단순한 로그인 우회 공격을 넘어서
전체 시스템을 무너뜨릴 수 있는 취약점입니다.
5️⃣ SQL 인젝션을 방어하는 기본 원칙
방어는 크게 3가지입니다.
✔ (1) Prepared Statement 사용 (가장 중요)
? 또는 :value 같은 placeholder 사용
➡️ 입력값을 절대 SQL 코드로 해석하지 않음
➡️ 99% 해결됨
✔ (2) 입력값 validation + escaping
-
',",;,--,#등을 의미 없는 문자로 처리 -
숫자는 숫자인지 체크
-
길이 제한
-
화이트리스트 방식 필터링
✔ (3) 최소 권한 원칙 적용
웹 서비스에서 사용하는 DB 계정은 권한을 최소한만 부여해야 합니다.
예:
-
DROP TABLE 금지
-
UPDATE 제한
-
특정 테이블만 SELECT 가능하도록 제한
🔚 정리
SQL 인젝션은 다음과 같은 이유로 발생합니다:
사용자 입력을 SQL 쿼리에 직접 포함시키기 때문이며,
데이터와 코드가 구분되지 않아 입력값이 SQL 명령처럼 동작하기 때문이다.
그리고 제대로 된 방어는:
Prepared Statement 사용 + 입력값 검증 + 최소 권한 설정
이 세 가지로 요약됩니다.