SQL 인젝션
SQL Injection · SQLi · 에스큐엘 인젝션
은 외부 입력이 의 데이터가 아니라 쿼리 문법으로 해석될 때 생기는 취약점이다. 값 과 구조의 을 분리하면 입력이 쿼리 구조를 바꾸는 경로를 차단할 수 있다.
밑줄 용어에 마우스를 올리거나, 키보드로 선택하거나, 터치하면 설명을 볼 수 있습니다.
01무엇이고, 어떤 일을 하나
- 문자열 연결 지점에서 데이터와 명령의 경계를 찾는다.
- DB 계정 권한과 애플리케이션 권한의 차이를 분석한다.
- 매개변수화 후에도 정상 검색 의미와 권한 제한이 유지되는지 검증한다.
02어떤 순서로 동작하나
- 요청값 수신
서버가 GET·POST·세션 등 실제 입력 위치를 읽고 필요한 정규화와 형식 검증을 수행한다.
- 쿼리 구조 결정
개발자가 고정한 구조를 선택한다. 문자열 연결 방식은 외부 값이 문법에 들어갈 수 있는 지점이다.
- 매개변수 전달
prepare/execute 등 드라이버가 지원하는 인터페이스로 값을 전달한다. 값 을 테이블명·정렬 구문에 대신 쓰지 않는다.
- DB 권한으로 실행
DB가 애플리케이션 계정의 권한 안에서 문장을 처리한다. 가 DB의 모든 권한을 자동으로 우회하는 것은 아니다.
- 행과 응답 처리
결과 행을 업무 규칙에 맞게 사용하고 안전하게 출력한다. 단순히 행이 있다는 이유만으로 비밀번호 검증을 대신하지 않는다.
03먼저 알아둘 용어와 구성요소
- 쿼리 구조를 준비하고 값은 별도 매개변수로 전달하는 방식.
- 입력을 문법이 아닌 값으로 연결하는 처리.
- 오류나 결과가 직접 보이지 않아도 조건에 따른 응답 차이로 영향을 추정하는 유형.
- 정렬 열처럼 값 이 불가능한 구조를 미리 정한 선택지에서 고르는 방식.
- 데이터 저장·조회·권한·을 관리하는 데이터베이스 시스템.
- 열·테이블 이름 등 구조의 일부로 값 매개변수와 처리 방식이 다르다.
- 값 뒤에도 패턴 검색에서 의미가 남는 %·_ 등의 문자.
DVWA의 문자열 결합 쿼리와 결과 열을 연결하기
입력→ 문자열→DB 결과→화면의 연결을 읽고, 매개변수 이 바꾸는 경계를 설명한다.
실습 구성과 전제
- 격리된 의 폐기 가능한 데이터베이스를 사용한다.
- 사진은 구형 · 코드다. 현재 서비스에 복사하지 않고 원리 비교 자료로만 읽는다.
입력이 SQL 문법 안으로 들어가는 위치를 찾는다
- 소스에서 GET의 id를 읽은 뒤 WHERE user_id = '$id'에 그대로 넣는 흐름을 따라간다. 따옴표와 변수 값이 하나의 문자열로 합쳐진다.
- mysql_query 계열은 구형 API다. 보안 교정은 다른 함수 이름으로 바꾸는 것만이 아니라 값과 쿼리 구조를 분리해야 한다.
원본 크기로 보기 ↗- $_GET['id']가 SELECT 문자열에 연결되고 mysql_query로 실행된다.
- 반복문은 결과 열 first_name·last_name을 화면에 출력한다. DB 결과의 두 열과 표시 위치를 연결할 수 있다.
확인 기준문자열 결합 지점과 출력 지점을 각각 표시한다.
결과 열의 모양이 유지되는 이유를 읽는다
- 교육 화면은 UNION으로 결합된 두 열이 기존 이름·성 표시 위치에 나타나는 사례다. 결과 집합의 열 수와 호환 가능한 형식이 맞아야 결합될 수 있다.
- 조회에 사용하는 DB 계정의 권한이 넓으면 애플리케이션이 의도하지 않은 자료도 결과에 섞일 수 있다. 화면의 테스트 해시를 다시 배포하지 않는다.
원본 크기로 보기 ↗- User ID 입력 뒤 여러 행이 표시되고 First name·Surname 위치에 계정명과 해시 형태의 값이 나타난다.
- 표시 레이블은 여전히 이름·성이다. 화면 레이블이 데이터의 실제 의미나 안전성을 보증하지 않는다.
확인 기준한 결과 화면만으로 데이터베이스 전체 권한이나 모든 공격 유형의 성공을 일반화하지 않는다.
정상 조회를 유지하면서 구조를 고정한다
- 준비된 쿼리의 값 자리에 사용자 입력을 바인딩한다. 정렬 열처럼 바인딩할 수 없는 구조는 으로 고른다.
- 정상 ID·존재하지 않는 ID·따옴표 포함 입력을 비교하고 오류 내용이나 다른 행이 노출되지 않는지 확인한다.
cursor.execute('SELECT first_name, last_name FROM users WHERE user_id = %s', (user_id,))DB-API 드라이버가 %s 을 지원한다는 조건의 비교 코드. 문자열 포매팅으로 직접 치환하지 않는다.
확인 기준정상 기능이 유지되고 입력이 쿼리 문법으로 해석되지 않아야 한다.
실습에서 확인한 동작 원리
- UNION 결과는 기존 출력 열을 통해 보일 수 있다.
- 과 DB 최소 권한은 서로 다른 방어층이다.
04예제로 따라가기
- 조회 구조: 가상 쿼리는 SELECT id FROM people WHERE name = ? 이고 값은 O'Reilly라고 가정한다.
- 결과: 작은따옴표는 문자열 값의 일부로 전달된다. 사용자가 보낸 문자 때문에 WHERE 뒤의 구조가 새로 만들어지지 않는다.
- 패턴 검색: 쿼리를 name LIKE ?로 바꾸고 A%를 전달하면 A로 시작하는 이름을 찾는다. 이는 정상 LIKE 의미이며 주입 성공의 증거가 아니다.
- 리터럴 요구: 실제 A%라는 이름만 찾으려면 DB의 ESCAPE 문법과 애플리케이션 정책에 맞춰 패턴 특수 문자를 처리해야 한다.
- 관찰 결과: 결과 행· 값·고정 쿼리 형태를 비교한다. 오류가 안 났다는 사실만으로 쿼리 구조가 안전하게 고정되었는지 알 수는 없다.
05혼동하기 쉬운 점과 확인 기준
- API 혼용: 일부 조건은 바인딩하면서 정렬·검색 연산자·조건 조각을 외부 입력으로 연결하고 있지 않은지 최종 쿼리 구성을 본다.
- 타입 변환: 문자열과 숫자의 자동 변환·NULL·빈 문자열이 DB별로 다르게 비교될 수 있다. 타입과 업무 규칙을 입력 검증에 명시한다.
- 연결 설정: 문자셋·드라이버의 에뮬레이션 설정·다중 문장 허용 등 실제 연결 옵션을 확인한다. 다른 환경의 결과를 그대로 적용하지 않는다.
06상세 노트 · 실습 자료
헤더 구조, 설정 예시, 수치 계산과 수업 캡처는 아래 노트에서 이어집니다.
함께 보는 학습 자료
아래 공식 자료를 본문의 개념·예제와 함께 읽어 보세요. 자료별로 확인할 위치와 관찰할 내용을 적었습니다.
OWASP: 값 바인딩과 SQL 구조의 경계
안전한 PreparedStatement 예제와 동적 테이블 이름 예제를 대조하여 값 이 가능한 부분을 구분한다.
- 질의 틀과 setString 등 값 전달의 분리
- 테이블·열·정렬 방향의 허용목록
- 저장 프로시저 내부 동적 의 예외
공식 문서 · 표준
기초 개념과 동작 설명을 대조한 자료입니다. 제품 버전과 실제 설정에 따른 차이는 본문 및 원문에서 확인하세요.