SQL 삽입공격이란?
SQL 삽입공격은 웹에플리케이션에 대해서 가해지는 가장 흔한 공격 기법 중의 하나로 에플리케이션의 허점을 이용해서 데이터베이스를 비정상적으로 조작하는 기법이다.
실습준비
실습을 해보자. 아래의 디비를 만들고 아래의 SQL문을 실행한다.
1 2 3 4 5 6 7 8 9 10 11 | CREATE TABLE `sql_injection` ( `id` int (11) NOT NULL AUTO_INCREMENT, `description` text NOT NULL , `type` enum( 'public' , 'private' ) NOT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; INSERT INTO `sql_injection` VALUES (1, 'public 1' , 'public' ); INSERT INTO `sql_injection` VALUES (2, 'public 2' , 'public' ); INSERT INTO `sql_injection` VALUES (3, 'private 3' , 'private' ); INSERT INTO `sql_injection` VALUES (4, 'private 4' , 'private' ); |
에플리케이션 코드
아래와 같은 코드를 가진 php 에플리케이션을 하나 만들자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <html> <body> <?php mysql_connect( 'localhost' , 'egoing' , '111111' ); mysql_select_db( 'advanced_php' ); $sql = "SELECT * FROM SQL_Injection WHERE type='" . $_GET ['type ']."' "; //$sql = "SELECT * FROM SQL_Injection WHERE type='".mysql_real_escape_string($_GET['type'])."'"; echo "SQL : $sql" ; ?> <ul> <?php $result = mysql_query( $sql ); while ( $row = mysql_fetch_array( $result )){ echo "<li>{$row['description']}</li>" ; } ?> </ul> </body> </html> |
위의 PHP 에플리케이션으로 접근하는 정상적인 URL은 아래와 같다.
그런데 사용자가 아래와 같이 URL을 바꾼다면 어떻게 될까?
위의 URL로 접근 했을 때 php 에플리케이션 내부적으로 생성되는 SQL문은 아래와 같다.
1 | SELECT * FROM SQL_Injection WHERE type= 'public' or 1= '1' |
하나씩 보자.
id의 값으로 아래 값이 전달 되었다.
1 | public' or 1='1 |
이 값은 PHP 코드 상에서 아래의 코드로 전달된다.
1 | id='".($_GET['type'])."'"; |
값을 코드에 대입하면 아래와 같은 형태가 된다.
1 | type='public' or 1='1'"; |
WHERE 조건에 OR가 추가 됐고, 1=1이라는 언제나 참인 명제를 WHERE 조건으로 추가했다. 그 결과 데이터베이스는 모든 정보를 출력하게 된다.
php 코드를 바꿔보자.
위의 예제에서 8번째 라인의 주석을 해제하고, 7라인에 주석을 적용해보자. 코드는 아래와 같이 된다.
1 2 | //$sql = "SELECT * FROM SQL_Injection WHERE type='".$_GET['type']."'"; $sql = "SELECT * FROM SQL_Injection WHERE type='" .mysql_real_escape_string( $_GET ['type '])."' "; |
동일한 URL로 접속을 시도해보자.
1 | SELECT * FROM SQL_Injection WHERE type= 'public\' or 1=\'1' |
아무 결과도 출력하지 않는다.
SQL 삽입공격은 많은 패턴이 존재한다. 이 모든 것을 다 이해할 수 있다면 좋겠지만, mysql_real_escape_string을 사용하는 것만으로도 많은 공격을 차단할 수 있다.
mysql_real_escape_string을 사용하는 것만으로도 대부분의 공격을 차단할 수 있다.
다음은 PHP에서는 객체지향적으로 mysql에 접근 할 수 있는 방법을 지원한다. 다음은 mysqli 를 이용해서 데이터베이스에 접근하는 방법을 보여주는 예제다. mysqli의 prepared statments 기능을 이용하면 자동으로 escaping을 해주기 때문에 이를 위한 특별한 처리를 하지 않아도 된다. mysql_query 보다 성능도 좋고, 보안도 강화된 방식이기 때문에 권장된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <html> <body> <ul> <?php $mysqli = new mysqli( "localhost" , "egoing" , "111111" , "advanced_php" ); $stmt = $mysqli->prepare( "SELECT * FROM SQL_Injection WHERE type=?" ); $stmt->bind _ param( "s" , $ _ GET[ 'type' ]); $stmt->execute(); $stmt->bind _ result($id, $description, $ type ); while ($stmt->fetch()){ print( "<li>$description</li>" ); } $stmt->close(); $mysqli->close(); ?> </ul> </body> </html> |
참고
- SQL Injection Cheatsheet
- PHP 보안 (O'REILLY) p.62
- PHP mysqli