Q: 사용자 입력을 검증 없이 SQL 쿼리 문장에 직접 삽입하면 SQL 인젝션 취약점이 발생할 수 있습니다. 다음 예시를 살펴보겠습니다:
$위험한_변수 = $_POST['사용자_입력'];
mysql_query("INSERT INTO `테이블` (`열`) VALUES ('$위험한_변수')");
왜 인젝션 취약점이 발생하는 것일까요? 사용자가 값'); DROP TABLE 테이블;-- 와 같은 값을 입력하면, 쿼리 문장은 다음과 같이 변형됩니다:
INSERT INTO `테이블` (`열`) VALUES('값'); DROP TABLE 테이블;--')
A: 프리페어드 스테이트먼트(prepared statements)와 파라미터화 쿼리(parameterized queries)를 사용하여 방지할 수 있습니다.
이를 구현하는 두 가지 방법은 다음과 같습니다:
1. PDO 객체 사용 (모든 데이터베이드 드라이버에 호환)
$stmt = $pdo->prepare('SELECT * FROM 직원 WHERE 이름 = :이름');
$stmt->execute(array('이름' => $이름));
2. MySqli 사용
$stmt = $db연결->prepare('SELECT * FROM 직원 WHERE 이름 = ?');
$stmt->bind_param('s', $이름);
$stmt->execute();
================
일반적으로 다음과 같은 방법들이 사용됩니다:
(1)입력 검증 및 필터링 (2)SQL 문장 사전 처리 (3)저장 프로시저 사용 (4)입력 화이트리스트 적용 (5)간단한 필터링, PHP의 addslashes 함수 사용
전체적인 인젝션 방지:
function 인젝션_체크($sql문) {
return eregi('select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $sql문); // 필터링 수행
}
function 아이디_검증($아이디=null) {
if (!$아이디) { exit('매개변수가 제출되지 않았습니다!'); } // 빈값 확인
elseif (인젝션_체크($아이디)) { exit('제출된 매개변수가 불법입니다!'); } // 인젝션 확인
elseif (!is_numeric($아이디)) { exit('제출된 매개변수가 불법입니다!'); } // 숫자 확인
$아이디 = intval($아이디); // 정수형 변환
return $아이디;
}<br></br><br></br>
function 문자열_검증( $문자열 ) {
if (!get_magic_quotes_gpc()) { // magic_quotes_gpc 상태 확인
$문자열 = addslashes($문자열); // 필터링 수행
}
$문자열 = str_replace("_", "\_", $문자열); // '_' 필터링
$문자열 = str_replace("%", "\%", $문자열); // '%' 필터링
return $문자열;
}<br></br><br></br>
function 포스트_검증($포스트) {
if (!get_magic_quotes_gpc()) { // magic_quotes_gpc 상태 확인
$포스트 = addslashes($포스트); // 필터링 수행
}
$포스트 = str_replace("_", "\_", $포스트); // '_' 필터링
$포스트 = str_replace("%", "\%", $포스트); // '%' 필터링
$포스트 = nl2br($포스트); // 줄바꿈 변환
$포스트 = htmlspecialchars($포스트); // HTML 태그 변환
return $포스트;
}<br></br><br></br>
/**
* 삽입 또는 업데이트할 필드 값 이스케이프
*
* 모든 쿼리와 업데이트 필드 변수는 이 메서드를 호출하여 데이터를 처리해야 합니다
*
* @param mixed $str 처리할 변수
* @return mixed 이스케이프된 결과 반환
*/
public function 이스케이프($문자) {
if (is_array($문자)) {
foreach ($문자 as $키 => $값) {
$문자[$키] = $this->이스케이프($값);
}
} else {
return addslashes($문자);
}
return $문자;
}<br></br><br></br>
사용 예시:
public function _저장_조건부($테이블이름, $데이터, $조건, $동기화 = false) {
// 삽입/업데이트할 필드 SQL 문자열 생성
$값들 = '';
foreach ($데이터 as $검색키 => $값) {
$값들 .= "`{$검색키}` = '{이스케이프($값)}',";
}
$값들 = trim($값들, ",");
// 조건이 있으면 UPDATE, 없으면 수행
if (trim($조건)) {
$쿼리 = "UPDATE {$테이블이름} SET {$값들} WHERE {$조건} ";
}else {
$쿼리 = "INSERT INTO {$테이블이름} SET {$값들}";
}
$this->로그저장($쿼리);
// lib_DB->update는 boolean만 반환, insert 시 last_id 필요할 경우 문제 발생
return $this->_업데이트($쿼리, $동기화);
}
기타 중요 사항:
1. http://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php