>  기사  >  백엔드 개발  >  PHP 데이터베이스 보안 - SQL 주입 및 예방 조치

PHP 데이터베이스 보안 - SQL 주입 및 예방 조치

伊谢尔伦
伊谢尔伦원래의
2016-11-22 10:45:541233검색

많은 웹 개발자는 SQL 쿼리가 변조될 수 있다는 사실을 깨닫지 못하여 SQL 쿼리를 신뢰할 수 있는 명령으로 취급합니다. 모두가 알고 있듯이 SQL 쿼리는 액세스 제어를 우회하여 인증 및 권한 확인을 우회할 수 있습니다. 또한 SQL 쿼리를 통해 호스트 운영 체제 수준 명령을 실행할 수도 있습니다.

직접 SQL 명령 주입은 공격자가 숨겨진 데이터를 얻거나, 키 값을 덮어쓰거나, 데이터베이스 호스트 운영 체제 명령을 실행하기 위해 기존 SQL 문을 생성 또는 수정하는 데 일반적으로 사용되는 기술입니다. 이는 애플리케이션이 사용자 입력을 가져와 이를 정적 매개변수와 결합하여 SQL 쿼리로 수행함으로써 수행됩니다. 몇 가지 실제 예가 아래에 제공됩니다.

입력된 데이터에 대한 유효성 검사가 부족하고 슈퍼유저 또는 새 사용자 생성 권한이 있는 다른 데이터베이스 계정을 사용한 연결로 인해 공격자가 데이터베이스에 새 슈퍼유저를 생성할 수 있습니다.

예제 #1 데이터의 페이징 표시를 구현하는 코드... 슈퍼유저(PostgreSQL 시스템)를 생성하는 데에도 사용할 수 있습니다.

<?php
    $offset = $argv[0]; // 注意,没有输入验证!
    $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
    $result = pg_query($conn, $query);
?>

일반 사용자는 $offset이 비닝된 "이전 페이지"와 "다음 페이지" 링크를 클릭하게 됩니다. 원래 코드에서는 $offset이 숫자 값인 것으로만 생각합니다. 그러나 누군가가 다음 명령문을 urlencode()하려고 시도한 다음 이를 URL에 추가하면:

0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select &#39;crack&#39;, usesysid, &#39;t&#39;,&#39;t&#39;,&#39;crack&#39;
    from pg_shadow where usename=&#39;postgres&#39;;
--

그는 슈퍼 유저를 생성할 수 있습니다. 0은 오류가 발생하지 않도록 원래 쿼리를 완료하기 위해 올바른 오프셋을 제공하는 것입니다.

참고:

--는 SQL 주석 표시로, 일반적으로 SQL 해석기가 다음 명령문을 무시하도록 지시하는 데 사용할 수 있습니다.

검색결과 페이지를 활용하는 것도 비밀번호를 얻을 수 있는 방법입니다. 공격자가 해야 할 일은 SQL 문에 어떤 변수가 제출되어 잘못 처리되었는지 알아내는 것뿐입니다. 이러한 변수는 일반적으로 WHERE, ORDER BY, LIMIT 및 OFFSET과 같은 SELECT 쿼리의 조건문에 사용됩니다. 데이터베이스가 UNION 구문을 지원하는 경우 공격자는 원본 문에 완전한 SQL 쿼리를 추가하여 임의의 데이터 테이블에서 암호를 얻을 수도 있습니다. 따라서 비밀번호 필드를 암호화하는 것이 중요합니다.

예제 2 기사 표시... 및 일부 비밀번호(모든 데이터베이스 시스템)

<?php
    $query = "SELECT id, name, inserted, size FROM products
        WHERE size = &#39;$size&#39;
        ORDER BY $order LIMIT $limit, $offset;";
    $result = odbc_exec($conn, $query);
?>

원래 쿼리에 다른 SELECT 쿼리를 추가하여 비밀번호를 얻을 수 있습니다.

&#39;
union select &#39;1&#39;, concat(uname||&#39;-&#39;||passwd) as name, &#39;1971-01-01&#39;, &#39;0&#39; from usertable;
--

$query의 변수에 위의 구문(' 및 -- 사용)을 추가하면 문제가 발생합니다.

SQL의 업데이트도 취약합니다. 이 쿼리는 위의 예와 같이 다른 완전한 요청에 삽입되거나 추가될 수도 있습니다. 그러나 공격자는 테이블의 일부 데이터를 변경할 수 있도록 SET 절을 표적으로 삼는 것을 선호합니다. 이 경우 쿼리를 성공적으로 수정하려면 데이터베이스의 구조를 알아야 합니다. 필드는 양식의 변수 이름을 기반으로 추측하거나 무차별 암호 대입 공격을 통해 추측할 수 있습니다. 사용자 이름과 비밀번호를 저장하는 필드의 이름을 지정하는 방법은 많지 않습니다.

예 #3 비밀번호 재설정에서 더 많은 권한 얻기까지(모든 데이터베이스 시스템)

<?php
    $query = "UPDATE usertable SET pwd=&#39;$pwd&#39; WHERE uid=&#39;$uid&#39;;";
?>

그러나 악의적인 사용자는 '%admin%'와 같은 uid를 사용합니다. $uid를 변수 값으로 제출하여 관리자 비밀번호를 변경하거나 $pwd 값을 "hehehe', admin='yes',trusted=100"(뒤에 공백 포함)으로 제출하여 더 많은 권한을 얻습니다. 이렇게 하면 쿼리는 다음과 같이 됩니다.

<?php
    // $uid == &#39; or uid like&#39;%admin%&#39;; --
    $query = "UPDATE usertable SET pwd=&#39;...&#39; WHERE uid=&#39;&#39; or uid like &#39;%admin%&#39;; --";
    // $pwd == "hehehe&#39;, admin=&#39;yes&#39;, trusted=100 "
    $query = "UPDATE usertable SET pwd=&#39;hehehe&#39;, admin=&#39;yes&#39;, trusted=100 WHERE
        ...;";
?>

다음 끔찍한 예에서는 일부 데이터베이스에서 시스템 명령을 실행하는 방법을 보여줍니다.

예제#4 데이터베이스가 위치한 호스트(MSSQL Server)의 운영체제를 공격한다.

<?php
    $query  = "SELECT * FROM products WHERE id LIKE &#39;%$prod%&#39;";
    $result = mssql_query($query);
?>

공격이 %' exec master..xp_cmdshell 'net user에게 제출되면 test testpass /ADD' -- 변수 $prod의 값으로 $query는

<?php
    $query = "SELECT * FROM products WHERE id LIKE &#39;%a%&#39;
        exec master..xp_cmdshell &#39;net user test testpass /ADD&#39;--";
    $result = mssql_query($query);
?>

이 됩니다. MSSQL 서버는 시스템에 사용자를 추가하기 위한 명령을 포함하여 이 SQL 문을 실행합니다. 이 프로그램이 sa로 실행되고 MSSQLSERVER 서비스에 충분한 권한이 있는 경우 공격자는 호스트에 액세스하기 위한 시스템 계정을 얻을 수 있습니다.

참고:

위의 예는 특정 데이터베이스 시스템에 대한 것이지만 다른 데이터베이스 시스템에서도 유사한 공격을 수행할 수 없다는 의미는 아닙니다. 다양한 방법을 사용하면 다양한 데이터베이스가 문제를 겪을 수 있습니다.

예방조치

위 공격을 수행하려면 공격자가 데이터베이스 구조에 대한 정보를 알아야 한다고 위로하는 사람도 있을 것이다. 예, 그렇습니다. 그러나 공격자가 이 정보를 얻지 못할 것이라고 누구도 보장할 수 없습니다. 일단 그렇게 되면 데이터베이스가 유출될 위험이 있습니다. 포럼 프로그램과 같은 데이터베이스에 접근하기 위해 오픈 소스 소프트웨어 패키지를 사용하는 경우 공격자가 관련 코드를 쉽게 얻을 수 있습니다. 코드가 잘못 설계되면 위험은 더욱 커집니다.

这些攻击总是建立在发掘安全意识不强的代码上的。所以,永远不要信任外界输入的数据,特别是来自于客户端的,包括选择框、表单隐藏域和 cookie。就如上面的第一个例子那样,就算是正常的查询也有可能造成灾难。

永远不要使用超级用户或所有者帐号去连接数据库。要用权限被严格限制的帐号。

检查输入的数据是否具有所期望的数据格式。PHP 有很多可以用于检查输入的函数,从简单的变量函数和字符类型函数(比如 is_numeric(), ctype_digit())到复杂的Perl 兼容正则表达式函数都可以完成这个工作。

如果程序等待输入一个数字,可以考虑使用 is_numeric() 来检查,或者直接使用 settype() 来转换它的类型,也可以用 sprintf() 把它格式化为数字。

Example #5 一个实现分页更安全的方法

<?php
    settype($offset, &#39;integer&#39;);
    $query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
    // 请注意格式字符串中的 %d,如果用 %s 就毫无意义了
    $query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
        $offset);
?>

使用数据库特定的敏感字符转义函数(比如 mysql_escape_string() 和 sql_escape_string())把用户提交上来的非数字数据进行转义。如果数据库没有专门的敏感字符转义功能的话 addslashes() 和 str_replace() 可以代替完成这个工作。看看第一个例子,此例显示仅在查询的静态部分加上引号是不够的,查询很容易被攻破。

要不择手段避免显示出任何有关数据库的信心,尤其是数据库结构。

也可以选择使用数据库的存储过程和预定义指针等特性来抽象数库访问,使用户不能直接访问数据表和视图。但这个办法又有别的影响。

除此之外,在允许的情况下,使用代码或数据库系统保存查询日志也是一个好办法。显然,日志并不能防止任何攻击,但利用它可以跟踪到哪个程序曾经被尝试攻击过。日志本身没用,要查阅其中包含的信息才行。毕竟,更多的信息总比没有要好。


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:PHP 생성자다음 기사:PHP 생성자