使用PHP和Zend Framework防止SQL注入 - 如何?

use*_*136 6 php mysql zend-framework sql-injection

我正试图通过SQL注入来保护我页面上的登录表单.在服务器端,我使用Zend框架(Zend_Db,Zend_Db_Table_Abstract),但其内置的注射预防功能:quote,quoteInto,quoteIdentifier不要让自己的工作做好(据我知道如何使用它们).其他方式mysql_real_escape_string,addslashes似乎根本不起作用......

这就是我正在尝试为防御实现的:

function prevent_from_sql_injection($str) {
    if(preg_match('/[\'"]/', $str))
     {die('attack1'); exit;  }// no quotes
elseif(preg_match('/[\/\\\\]/', $str))
     {die('attack2'); exit;  }// no slashes
elseif(preg_match('/(and|or|null|not)/i', $str))
     {die('attack3'); exit;  }// no sqli boolean keywords
elseif(preg_match('/(union|select|from|where)/i', $str))
     {die('attack4'); exit;  }// no sqli select keywords
elseif(preg_match('/(group|order|having|limit)/i', $str))
     {die('attack5'); exit;  }//  no sqli select keywords
elseif(preg_match('/(into|file|case|LOAD_FILE|DUMPFILE|char|schema|AES_DECRYPT|AES_ENCRYPT)/i', $str))
     {die('attack6'); exit;  }// no sqli operators
elseif(preg_match('/(--|#|\/\*)/', $str))
     {die('attack7'); exit; }// no sqli comments
elseif(preg_match('/(=|&|\|)/', $str))
     {die('attack8'); exit;  }// no boolean operators
elseif(preg_match('/(UNI\*\*ON|1 OR 1=1|1 AND 1=1|1 EXEC XP_)/', $str))
     {die('attack9'); exit; }
elseif(preg_match('/(1|'| |O|R|=|&#49&#39&#32&#79&#82&#32&#39&#49&#39&#61&#39&#49|%31%27%20%4F%52%20%27%31%27%3D%27%31)/', $str))
     { die('attack10'); exit; }
elseif(preg_match('/(SELECT\s[\w\*\)\(\,\s]+\sFROM\s[\w]+)| (UPDATE\s[\w]+\sSET\s[\w\,\'\=]+)| (INSERT\sINTO\s[\d\w]+[\s\w\d\)\(\,]*\sVALUES\s\([\d\w\'\,\)]+)| (DELETE\sFROM\s[\d\w\'\=]+)/', $str))
     { die('attack11'); exit; } 
elseif(preg_match('/(script)|(<)|(>)|(%3c)|(%3e)|(SELECT) |(UPDATE) |(INSERT) |(DELETE)|(GRANT) |(REVOKE)|(UNION)|(<)|(>)/', $str))
     { die('attack12'); exit; } 
elseif(!preg_match('/^["a-zA-Z0-9\040]+$/', $str))
     { die('attack13'); exit; } 
else return $str;

}
Run Code Online (Sandbox Code Playgroud)

至于测试我的结果,我使用Firefox扩展SQL Inject Me,它显示了14个错误(有时是21或17,我不知道为什么结果不同):

Server Status Code: 302 Found
Tested value: 1' OR '1'='1
Server Status Code: 302 Found
Tested value: 1 UNI/**/ON SELECT ALL FROM WHERE
Server Status Code: 302 Found
Tested value: &#49&#39&#32&#79&#82&#32&#39&#49&#39&#61&#39&#49
Server Status Code: 302 Found
Tested value: 1 OR 1=1
Server Status Code: 302 Found
Tested value: 1' OR '1'='1
Server Status Code: 302 Found
Tested value: 1 EXEC XP_
Server Status Code: 302 Found
Tested value: 1 UNION ALL SELECT 1,2,3,4,5,6,name FROM sysObjects WHERE xtype = 'U' --
Server Status Code: 302 Found
Tested value: %31%27%20%4F%52%20%27%31%27%3D%27%31
Server Status Code: 302 Found
Tested value: 1 AND 1=1
Server Status Code: 302 Found
Tested value: 1' OR '1'='1
Server Status Code: 302 Found
Tested value: 1 AND ASCII(LOWER(SUBSTRING((SELECT TOP 1 name FROM sysobjects WHERE xtype='U'), 1, 1))) > 116
Run Code Online (Sandbox Code Playgroud)

那么防止所有这些SQL注入攻击的最佳方法是什么?使用占位符是好的,但在某些情况下它并不好.也许这个扩展是错误的,我有一个偏执狂?

Ben*_*mer 8

我强烈建议使用Zend_DB.它使用预准备语句.
准备语句的参数不需要引用; 驱动程序自动处理这个.

如果应用程序专门使用预准备语句,开发人员可以确保不会发生SQL注入(但是,如果查询的其他部分是使用非转义输入构建的,则仍然可以进行SQL注入

$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
));

$stmt = $db->query('SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?',
    array('goofy', 'FIXED')
);

$rows = $stmt->fetchAll();

echo $rows[0]['bug_description'];
Run Code Online (Sandbox Code Playgroud)


mar*_*rio 7

使用准备好的SQL语句而不是值转义.

 $st = $pdo->prepare("SELECT * FROM tbl WHERE x = ?");
 $st->execute($x);
Run Code Online (Sandbox Code Playgroud)

将几个SQL语句列入黑名单并不是一种明智的方法.Firefox扩展有误导性并给你错误的印象.

显然你不应该开始接受随机SQL命令作为输入.最多使用一些请求变量作为查询数据.准备好的陈述足以为该案件提供安全保障.连接SQL命令和变量是一种过时的做法(Firefox扩展似乎暗示了什么.).