我正试图通过sql注入来保护我页面上的登录表单.在服务器端,我使用Zend Framework(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|=|1' OR '1'='1|%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)|(&lt;)|(&gt;)/',$str))
{ die('attack12'); exit; }
elseif(!preg_match('/^["a-zA-Z0-9\040]+$/',$str))
{ die('attack13'); exit; }
else return $str;
}
至于测试我的结果,我使用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: 1' OR '1'='1
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
那么防止所有这些sql注入攻击的最佳方法是什么?使用占位符是好的,但在某些情况下它并不好.也许这个扩展是错误的,我有一个偏执狂?
最佳答案
我强烈建议使用Zend_DB.它使用prepared statements.
准备语句的参数不需要引用;驱动程序自动处理这个.
准备语句的参数不需要引用;驱动程序自动处理这个.
If an application exclusively uses
prepared statements,the developer can
be sure that no sql injection will
occur (however,if other portions of
the query are being built up with
unescaped input,sql injection is
still possible
$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'];