[2달 4주차 주제 : SQL Injection: Advanced]
1. SQL Injection 유형
1) Union Based & Error Based & Blind SQLi
- 3가지 경우 모두 우리가 원하는 select문을 실행하는 것이 핵심이다.
▶ 우리가 실행할 SQL문이 명확해야 한다.
2) Union Based SQLi 특징
- SQL 질의문의 결과가 화면에 출력되는 경우 사용
3) Error Based SQli 특징
- SQL 에러 메시지가 응답에 포함되는 경우 사용
4) Blind SQLi 특징
- SQL Injection 포인트가 존재하는 모든 경우에서 사용 가능
2. SQL Injection 포인트 찾기
[SQLi 발생 위치]
: DB에게 SQL 질의문을 사용하는 곳
▶ 화면에 보이는 데이터가 고정된 것인지 혹은 DB로부터 데이터를 가져와 출력하는 곳인지 확인 후 어떤 파라미터가 영향을 미치는지 캐치한다.
[SQLi 포인트 단계별 정리]
| (1) 아이디를 입력하면, SQL 질의문을 통해 데이터를 가져와 출력하는 것을 확인 |
|---|
 |
| (2) SQL 질의문 추측 |
|---|
| select (컬럼) from (테이블) where user_id like '%___%' |
| (3) SQL 질의문 삽입 가능 여부 확인 |
|---|
 |
| ○ nor 검색 결과와 nor%' and '1%'='1 의 검색 결과가 동일. 즉, SQLi를 수행할 수 있다고 판단 |
| (4) 참 / 거짓 조건 실행 결과 확인 |
|---|
 |
 |
▶ 그러나 사용자의 입력을 통해서만 SQL Injection 이 가능한 것은 아니다.
3. SQL Injection Case
[1. 쿠키값을 통한 SQLi]
| (1) 마이페이지 |
|---|
 |
| ○ 쿠키값을 통해 개인정보를 출력해주고 있다. |
| ● 즉, 쿠키값을 통해 DB에 질의하고 그 결과를 출력하고 있다. |
| (2) SQL 질의문 추측 |
|---|
| select (컬럼) from (테이블) where user_id = '쿠키 값' |
| (3) SQL 질의문 삽입 가능 여부 확인 |
|---|
 |
| ○ 생성되는 쿼리 : ~ where user_id='ttest' and '1'='1' |
| ● ttest 값을 전달한 결과와 동일 |
| (4) 참/거짓 조건 실행 결과 확인 |
|---|
 |
 |
| ○ 참/거짓 조건에 따라 유저의 정보 출력 여부가 차이난다. |
[2. HTTP 요청헤더(User-Agent)를 통한 SQL Injection]
| (1) 이용자의 IP와 User-Agent String 기록 |
|---|
 |
| (2) SQL 질의문 추측 |
|---|
| ('User-Agent String', 'IP') |
| (3) SQL 질의문 삽입 가능 여부 확인 |
|---|
 |
| ○ User-Agent: test', '1'='1')# |
| (4) 참/거짓 조건 실행 결과 확인 |
|---|
 |
 |
| ○ 참 조건일때 IP에 1, 거짓 조건일때 IP에 0으로 출력된다. |
[3. 컬럼 이름을 통한 SQL Injection]
| (1) 게시판에서 작성자 이름으로 게시글 검색 |
|---|
 |
 |
| (2) SQL 질의문 추측 |
|---|
| select (컬럼) from (테이블) where option_val like '%board_result%' |
| (3) SQL 질의문 삽입 가능여부 확인 |
|---|
 |
| ○ option_val : '1'='1' and username |
| (4) 참/거짓 조건 실행 결과 확인 |
|---|
 |
 |
| ○ 참/거짓 조건에 따라 게시글 출력 여부가 차이난다. |
[4. 정렬을 통한 SQL Injection]
| (1) 게시판에서 게시글 제목으로 게시글 검색 |
|---|
 |
 |
| (2) SQL 질의문 추측 |
|---|
| select (컬럼) from (테이블) where option_val like '%board_result%' order by sort |
| ○ order by 부분에 참/거짓 조건을 삽입하기 위하여 case when 문법이 사용된다. |
| ● case when (조건) then (참일때 실행) else (거짓일때 실행) end |
| (3) SQL 질의문 삽입 가능여부 확인 |
|---|
 |
| ○ sort : case when (1=1) then 1 else (select 1 union select 2) end |
| ● 조건이 참일때 order by 1, 조건이 거짓일때 행렬이 되어 오류 발생 |
| (3-1) 오류 발생 추가적인 구문 |
|---|
 |
| ○ sort : (select 1 union select 2 where (1=1)) |
| ● 조건이 참일때 행렬이 되어 오류 발생, 조건이 거짓일때 order by 1 |
| (4) 참/거짓 조건 실행 결과 확인 |
|---|
 |
 |
| ○ 참/거짓 조건에 따라 게시글 출력 여부가 차이난다. |
[5. 참/거짓 조건의 결과가 동일할때 SQL Injection]
| (1) 마이 페이지 |
|---|
 |
| ○ 쿠키값을 통해 개인정보 출력 |
| (2) SQL 질의문 추측 |
|---|
| select (컬럼) from (테이블) where user_id = '쿠키 값' |
| (3) SQL 질의문 삽입 가능여부 확인 |
|---|
 |
 |
| ○ Cookie : ttest' and '1'='1 / ttest' and '1'='2 |
| ● 참/거짓 조건의 실행결과가 동일 |
| (4) 에러를 유발하여 결과값이 달라지도록 유도 |
|---|
 |
| ○ Cookie : ttest' and (select 1 union select 2 where (1=1)) and '1'='1 |
| (5) 참/거짓 조건 실행 결과 확인 |
|---|
 |
 |
| ○ 조건이 참일때 DB에러 발생, 조건이 거짓일때 개인정보 정상 출력 |
4. SQL Injection 대응 방안
1) Prepared Statement
Prepared Statement 란?
쿼리 실행계획 분석과 컴파일을 미리 수행하여 사용하는 방식이다.
바인딩 변수(?)를 사용하여 SQL의 반복되는 내용을 쉽게 구현 가능하다.
String prepareStatement = "select * from users where name=? and password=?";
PreparedStatement preparedStatement = connection.prepareStatement(prepareStatement);
preparedStatement.setString(1, loginName);
preparedStatement.setString(2, loginPassword);
Prepared Statement 장점
-
Statement 객체의 SQL 쿼리문은 실행될 때마다 분석해야 하는 반면 Prepared Statement 객체는 한번 분석되면 이후에 재사용이 용이하다.
-
각 인수에 대해 placeholder (?)를 사용하여 SQL문을 정의할 수 있으며, 이를 통해 SQL Injection 공격에 방어할 수 있다.
2) White List 기반의 필터링
-
Prepared Statement 방식을 사용하면 근본적으로 SQL Injection 방어가 가능하다.
-
그러나 order by, table 이름, 컬럼 이름 등에는 Prepared Statement 적용이 불가능하다.
-
이러한 경우 화이트리스트 기반의 필터링을 적용한다.
if (sort == 'title')
제목 기준 정렬
elif (sort == 'user_name')
작성자 이름 기준 정렬
else
조회수 기준 정렬