데이터베이스에 데이터를 조회하거나 입력, 수정하는 질의문을 우리는 SQL(Structured Query Language 혹은 쿼리)이라고 한다. 그리고 자주 사용되는 SQL을 데이터베이스가 이해하기 쉬운 형태로 해석해 놓은 것을 Prepared statement라고 한다. 이로써 별도의 해석 과정 없이 실행되는 Prepared statement는 다량의 쿼리를 처리해야 하는 중대형 시스템에서는 없어서는 안 될 중요한 기능 중의 하나이며, MySQL 데이터베이스는 5.0 버전부터 추가되어 PHP 개발자에게도 유용하게 사용되는 기능으로 자리 잡고 있다.
그렇다고 반드시 대규모 시스템이나 대량의 쿼리 처리에만 Prepared statement가 사용되는 것은 아니다. 데이터 처리량이 매우 소량인 단순한 웹 프로그램에도 Prepared statement를 사용하고 있으며, 그로 인하여 시스템 자원이 낭비되는 경우가 실존하고 있다. Prepared statement는 데이터베이스 세션 내에 해석된 문장을 미리 준비하기 위하여 변수의 할당 및 해석 작업을 내부적으로 진행하므로, 쿼리 처리량이 적은 경우에는 오히려 시스템 자원의 손해가 발생한다. 그러므로 소규모 시스템이나 데이터 처리량이 적은 프로그램에는 Prepared statement를 사용하지 않는 것이 효율적이다.
그렇다면, 이렇게 시스템 자원을 낭비할 수도 있는 Prepared statement를 굳이 소규모 시스템에서도 사용하는 이유는 무엇일까? 그것은 바로 Prepared statement를 사용함으로써 어느 정도의 SQL Injection에 대한 방어 효과가 있기 때문이다. 즉, Prepared statement를 사용하면 프로그램을 개발하는 입장에서 조금 더 손쉽게 웹 프로그램의 안정성을 높일 수 있는 효과가 있다.
다음과 같은 코드가 웹 프로그램 내에 존재한다고 가정해보자.
$query = "SELECT * FROM board LIMIT " . $_GET['limit'] ; $set = mysql_query ( $query ) ;
위의 예제 코드는 board 테이블(게시판)에서 레코드를 조회하는 프로그램 코드로 데이터 조회를 위한 쿼리를 생성 할 때 사용되는 $_GET[‘limit’] 변수를 아무런 검증 절차도 없이 사용되고 있다. 만약, 악의적인 생각을 가진 해커(크래커)가 $_GET[‘limit’]을 다음과 같이 변조하면 어떻게 될까?
$_GET['limit'] = '0;' . ENTER . 'insert into mysql.user value("%", "root", password("1234") ... 생략 ... );' ;
쿼리 문법상 오류가 없으므로 데이터베이스에 전달되어 정상적으로 실행될 것이며, 쿼리를 실행하는 계정(account)이 mysql 데이터베이스 접근 권한까지 가지고 있다면 root 권한은 해커에게 넘겨지게 된다. 즉, 회원 정보를 비롯하여 수 많은 정보가 순식간에 해커에게 넘어가고 그 이후에 발생되는 여러가지 복잡하고 머리 아픈 문제는 개발자를 비롯하여 회사와 동료 모두에게 고난의 시간이 될 것이다.
만약 아래와 같이 PDO 객체의 prepare 함수(Prepared statement)를 사용하여 프로그램이 개발되어 있다면, SQL Injection에 대한 걱정을 조금은 줄일 수 있다.
$stmt = $pdo->prepare ( "SELECT * FROM board LIMIT ?" ) ; $stmt->execute ( array ( (int) $_GET['limit'] ) ) ;
물론, 이와 같은 SQL Injection 공격은 내부 사정을 잘 알고 있는 사람에 의해 발생되는 것이 대부분이지만, 프로그램 오류에 의한 에러 정보 출력이나 AJAX를 비롯하여 예기치 못한 접근으로 데이터베이스 정보가 해커에게 노출되는 경우가 종종 있다.
그러므로 프로그래머는 예기치 못한 입력이나 접근, 해커의 공격을 감안하여 최대한 안정성이 높은 프로그램을 제작해야 한다. 이러한 작업을 도와주는 방법 중에 하나가 바로 Prepared statement의 사용인 것이다.