PHP에서 SQL 인젝션 방지 기술

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

태그: PHP 보안 SQL 인젝션 방지 프리페어드 스테이트먼트 입력 검증 파라미터화 쿼리

6월 6일 20:25에 게시됨