php
PHP - SQL 인젝션 공격
SQL 질의 공격이 현실적으로 많은 문제점을 가져 온다는 점에 최신 버전(MYSQL, MSSQL..)에서 자체적으로 필터링하고 있지만, 구버전에서는 그렇지 못합니다.
SQL 질의를 신뢰할 수 없는 명령으로 인해 SQL 질의에서 접근 제어를 우회할 수 있여, 일반적인 인증과 인증 확인을 무시하고, 종종 SQL 질의가 사용자가 가질 수 없는 권한을 강제 취득하기도 합니다.
SQL 명령 인젝션이란? 공격자가 숨겨진 데이터를 노출하거나, 취약한 부분을 덮어쓰거나, 데이터베이스에 위험한 시스템 단계 명령을 실행하게 하는 SQL 명령을 생성하거나 대체하는 기술를 말합니다.
어플리케이션이 사용자 입력을 받아서, 이를 SQL 질의를 만들 떄 정적 인수로 조합함으로써 일어납니다. 유감스럽게도, 아래 예제들은 실제의 것입니다.
패스워드를 얻는 방법 중 하나는 검색 결과 페이지를 우회하는 것입니다. 공격자에게 필요한 것은 변수 중 하나라도 제대로 다뤄지지 않으면서 SQL 구문에 사용되는 것입니다.
이러한 필터는 일반적으로 SELECT 구문에서 WHERE, ORDER BY, LIMIT, OFFSET에 사용됩니다. 데이터베이스가 UNION 구조를 지원하면, 공격자는 원래 질의에 전체 질의를 덧붙여서 임의의 테이블에서 패스워드를 얻을 수 있습니다. 암호화된 패스워드 필드를 강력히 권합니다.
다음 질의로 비밀번호없이 누구나 접속이 가능하게 변질되어 버립니다.
SQL UPDATE도 공격받을 수 있는데, 이런 질의를 완전한 새 질의를 덧붙일 수 있습니다. 또한 공격자가 SET 절을 다룰 수도 있습니다. 이 경우 질의를 성공적으로 변경하기 위하여 일부 스키마 정보를 가지고 있어야 합니다.
악의적인 사용자가 $uid 에 ' or uid like'%admin'; -- 값을 넣어서 관리자 패스워드를 변경하거나, $pwd 에 "hehehe', admin='yes', trusted=100 "(마지막 공백 포함)을 설정하여 권한을 얻을 수도 있습니다.
데이터베이스 호스트의 OS 등급 명령에 접근하는 문제시 될 예제입니다.
공격자가 $prod에 a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- 값을 제출하면, $query는:
이러한 공격은 주로 보안을 염두에 두지 않고, 쓰여진 코드 취약점에서 발생합니다. 어떠한 입력도 믿어서는 안되며, 최신 버전이라도 한번더 필터링해 주어야 합니다.
특히 클라이언트측에서 오는 입력은 믿어서는 안됩니다. select, hidden input 필드, 쿠키도 마찬가지입니다. 첫 번째, 두 번째 예제에서 그러한 질의가 큰 문제를 일으킬 수 있음을 보여주고 있습니다.
이러한 문제를 회피하기 위해 안전한 방법으로 문자열 회피 함수를 사용하는 것인데, mysql_real_escape_string() 가 그것입니다.
mysql_real_escape_string 는 mysql_query 에서 특수 문자열을 이스케이프하기 위해 사용되며, 그러므로 SQL 인젝션 공격이 동작하지 않고 질의가 정확하게 실행될 것입니다.
아니면 addslashes() 와 str_replace() 함수를 사용할 수도 있습니다. addslashes() 는 데이터베이스 질의 등에서 처리할 필요가 있는 문자 앞에 백슬래시를 붙인 문자열을 반환합니다. 이 문자들은 작은 따옴표('), 큰 따옴표("), 백슬래시(\), NUL(NULL 바이트)입니다.
addslashes()를 사용하는 대표적인 예는 데이터베이스에 데이터를 넣을 때 입니다. 예를 들어, 데이터베이스에 O'reilly 라는 이름을 넣으려고 할때, 이스케이프할 필요가 있습니다. 대부분의 데이터베이스는 \을 사용하기에 O\'reilly가 되어야 합니다. 이 데이터를 데이터베이스에 넣으면 추가한 \은 저장되지 않습니다.
SQL 질의를 신뢰할 수 없는 명령으로 인해 SQL 질의에서 접근 제어를 우회할 수 있여, 일반적인 인증과 인증 확인을 무시하고, 종종 SQL 질의가 사용자가 가질 수 없는 권한을 강제 취득하기도 합니다.
SQL 명령 인젝션이란? 공격자가 숨겨진 데이터를 노출하거나, 취약한 부분을 덮어쓰거나, 데이터베이스에 위험한 시스템 단계 명령을 실행하게 하는 SQL 명령을 생성하거나 대체하는 기술를 말합니다.
어플리케이션이 사용자 입력을 받아서, 이를 SQL 질의를 만들 떄 정적 인수로 조합함으로써 일어납니다. 유감스럽게도, 아래 예제들은 실제의 것입니다.
패스워드를 얻는 방법 중 하나는 검색 결과 페이지를 우회하는 것입니다. 공격자에게 필요한 것은 변수 중 하나라도 제대로 다뤄지지 않으면서 SQL 구문에 사용되는 것입니다.
이러한 필터는 일반적으로 SELECT 구문에서 WHERE, ORDER BY, LIMIT, OFFSET에 사용됩니다. 데이터베이스가 UNION 구조를 지원하면, 공격자는 원래 질의에 전체 질의를 덧붙여서 임의의 테이블에서 패스워드를 얻을 수 있습니다. 암호화된 패스워드 필드를 강력히 권합니다.
SQL 질의 공격
검증되지 않는 변수를 전적 사용자의 신뢰를 믿고 필터링하지 않는다면, 문제는 커질 수 밖에 없습니다. 다음 변수에 이 질의('와 --로..)가 $query 에서 사용하는 변수 중 하나에 할당되면, 문제는 커질 수 밖에 없습니다.<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = mysql_query($query);
?>
다음 질의로 비밀번호없이 누구나 접속이 가능하게 변질되어 버립니다.
<?php
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";
$query = "SELECT * FROM users WHERE user='{$_POST['username']}'
AND password='{$_POST['password']}'";
mysql_query($query);
echo $query;
// 결과: SELECT * FROM users WHERE user='aidan' AND password='' OR ''=''
?>
SQL UPDATE도 공격받을 수 있는데, 이런 질의를 완전한 새 질의를 덧붙일 수 있습니다. 또한 공격자가 SET 절을 다룰 수도 있습니다. 이 경우 질의를 성공적으로 변경하기 위하여 일부 스키마 정보를 가지고 있어야 합니다.
<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
악의적인 사용자가 $uid 에 ' or uid like'%admin'; -- 값을 넣어서 관리자 패스워드를 변경하거나, $pwd 에 "hehehe', admin='yes', trusted=100 "(마지막 공백 포함)을 설정하여 권한을 얻을 수도 있습니다.
<?php
// $uid == ' or uid like'%admin%'; --
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like
'%admin%'; --";
// $pwd == "hehehe', admin='yes', trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100
WHERE ...;";
?>
데이터베이스 호스트의 OS 등급 명령에 접근하는 문제시 될 예제입니다.
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
공격자가 $prod에 a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- 값을 제출하면, $query는:
<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
?>
이러한 공격은 주로 보안을 염두에 두지 않고, 쓰여진 코드 취약점에서 발생합니다. 어떠한 입력도 믿어서는 안되며, 최신 버전이라도 한번더 필터링해 주어야 합니다.
특히 클라이언트측에서 오는 입력은 믿어서는 안됩니다. select, hidden input 필드, 쿠키도 마찬가지입니다. 첫 번째, 두 번째 예제에서 그러한 질의가 큰 문제를 일으킬 수 있음을 보여주고 있습니다.
SQL 인젝션 회피
다음은 안전한 질의 예제가 됩니다.<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET
$offset;";
$query = sprintf("SELECT id, name FROM products ORDER BY
name LIMIT 20 OFFSET %d;", $offset);
?>
이러한 문제를 회피하기 위해 안전한 방법으로 문자열 회피 함수를 사용하는 것인데, mysql_real_escape_string() 가 그것입니다.
mysql_real_escape_string 는 mysql_query 에서 특수 문자열을 이스케이프하기 위해 사용되며, 그러므로 SQL 인젝션 공격이 동작하지 않고 질의가 정확하게 실행될 것입니다.
<?php
if (isset($_POST['product_name']) &&
isset($_POST['product_description']) &&
isset($_POST['user_id'])) {
// 접속
$link = mysql_connect(
'mysql_host',
'mysql_user',
'mysql_password'
);
if(!is_resource($link)) {
echo "서버 접속 실패\n";
// ... 오류를 적절히 기록
} else {
// ON일 경우 magic_quotes_gpc/magic_quotes_sybase 효과 제거
if(get_magic_quotes_gpc()) {
$product_name =
stripslashes($_POST['product_name']);
$product_description =
stripslashes($_POST['product_description']);
} else {
$product_name = $_POST['product_name'];
$product_description = $_POST['product_description'];
}
// 안전한 질의 만들기
$query = sprintf("INSERT INTO products (
`name`, `description`, `user_id`)
VALUES ('%s', '%s', %d)",
mysql_real_escape_string($product_name, $link),
mysql_real_escape_string($product_description, $link),
$_POST['user_id']);
mysql_query($query, $link);
if (mysql_affected_rows($link) > 0) {
echo "Product inserted\n";
}
}
} else {
echo "Fill the form property\n";
}
?>
아니면 addslashes() 와 str_replace() 함수를 사용할 수도 있습니다. addslashes() 는 데이터베이스 질의 등에서 처리할 필요가 있는 문자 앞에 백슬래시를 붙인 문자열을 반환합니다. 이 문자들은 작은 따옴표('), 큰 따옴표("), 백슬래시(\), NUL(NULL 바이트)입니다.
addslashes()를 사용하는 대표적인 예는 데이터베이스에 데이터를 넣을 때 입니다. 예를 들어, 데이터베이스에 O'reilly 라는 이름을 넣으려고 할때, 이스케이프할 필요가 있습니다. 대부분의 데이터베이스는 \을 사용하기에 O\'reilly가 되어야 합니다. 이 데이터를 데이터베이스에 넣으면 추가한 \은 저장되지 않습니다.
<?php
$str = "Is your name O'reilly?";
// 출력: Is your name O\'reilly?
echo addslashes($str);
?>
0 댓글