1. Prepared Statement
# 입력 받을 준비
select * from member where id=''
# 사용자 입력
user' and '1' = '1
# 입력 받은 값 컴파일
select * from member where id='user' and '1' = '1'
ㄴ> 01010101001010101010101010101010101
# 실행
01010101001010101010101010101010101
# SQLi 발생!
# SQL 쿼리문 구조 바뀜
사용자의 값을 입력받으면
SQL 질의문 (위의 예시에서는 select 구문) 전체를 사용자에게 전달받은 값과 함께 기계어로 컴파일한다.
이 때문에 SQL 인젝션이 발생했다.
# 사용자 입력 부분 제외 미리 컴파일
01010101010101010101 사용자 입력 값 010101010 # select * from member where id='?'
# 사용자 입력
user' and '1' = '1
# 실행
01010101010101010101 user' and '1' = '1 010101010
# SQL 쿼리문 구조가 변하지 않음
ex) 작은 따옴표 혹은 주석에 경우 어떤 기능이 있는 것이 아닌 문자 그대로 받아들여짐
하지만, prepared statement를 사용하면
사용자에게 입력받을 부분을 ? 기호를 이용해 해당 부분을 제외하고
나머지 SQL 질의문의 구조를 미리 기계어로 컴파일해 둔다.
이후 사용자의 입력 값을 문자열 그대로 받아들여 실행한다.
이 경우 준비해 둔 SQL 쿼리의 구조가 바뀌는 것이 원천적으로 불가능하기 때문에
SQL 인젝션 역시 불가능해진다.
2. White List Filtering
앞에 이야기한 Prepared Statement를 사용하지 못하는 장소가 있다.
이러한 장소에서는 화이트 리스트 필러팅을 통해 SQLi 를 제어할 수 있다.
2.1. order by
order by 1
order by title
위와 같이 order by 구문은 컬럼을 기준으로 정렬하기 때문에
컬럼 값이 필요하다.
하지만 컬럼 값은 prepared statement 적용이 불가능 하고, SQL 쿼리 구조에 직접 삽입되기 때문에
SQL 인젝션이 발생할 여지가 높다.
<?php
// 허용된 column 목록
$allowed_columns = array("username", "title", "content");
// 사용자 입력
$client_input = $_POST['column'];
// 사용자 입력 컬럼과 허용된 컬럼 검증
if (in_array($client_input, $allowed_columns)) {
// 허용된 column일 경우 -> 쿼리 실행
$sql = $conn->prepare("SELECT * FROM board ORDER BY $client_input");
} else {
// 허용되지 않은 column일 경우 -> 제목으로 정렬
$sql = $conn->prepare("SELECT * FROM users ORDER BY title");
}
?>
위와 같이
허용된 column 이름으로 사용자의 입력 값을 필터링 한다면
SQL 인젝션을 방지할 수 있다.
2.2. table, column 이름
select * from board where $column like '%$search%'
$column = 1=1 and title
$search = test
# 인젝션 발생
select * from board where 1=1 and title like '%test%'
마찬가지로 컬럼 값과 테이블 값은 prepared statement 적용이 불가능 하고, SQL 쿼리 구조에 직접 삽입된다.
<?php
// 사용자 입력 받기
$search_area = $_POST['search_area'];
// 검색할 column 선택
if ($search_area == 'title') {
$search_column = 'title';
} else if ($search_area == 'content') {
$search_column = 'content';
} else if ($search_area == 'username'){
$search_column = 'username';
} else {
// 검색 영역이 올바르지 않은 경우 기본값으로 제목을 선택
$search_column = 'title';
}
// Prepare statement 생성
$sql = $conn->prepare("SELECT * FROM board WHERE $search_column LIKE CONCAT('%', ?, '%')");
?>
위와 같이
허용된 column 이름으로 사용자의 입력 값을 필터링 한다면
SQL 인젝션을 방지할 수 있다.
'Web > Theory' 카테고리의 다른 글
SQLi 공백 우회 (0) | 2024.06.13 |
---|---|
SQLi 정리 (0) | 2024.06.13 |
SQLi 포인트 (1) | 2024.06.08 |
Blind SQLi (0) | 2024.05.31 |
Error Based SQLi (0) | 2024.05.30 |