【问题标题】:Preventing SQL injections with PHP and Zend Framework - how?使用 PHP 和 Zend 框架防止 SQL 注入 - 如何?
【发布时间】:2010-08-01 20:11:11
【问题描述】:

我试图保护我页面上的登录表单免受 SQL 注入。在服务器端,我使用 Zend Framework (Zend_Db,Zend_Db_Table_Abstract),但它的内置注入预防功能:quotequoteIntoquoteIdentifier 不能很好地工作(据我知道如何使用它们)。 mysql_real_escape_stringaddslashes 等其他方式似乎根本不起作用...

这是我试图为防御实施的:

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;

}

为了测试我的结果,我使用了 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

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

【问题讨论】:

  • 定义您提到的其他选项“效果不佳”。
  • 您是否认为其中一些只是临时报告?仅仅因为服务器返回302代码,并不意味着注入成功。
  • 也许你是对的,这个分机返回不正确的结果。但是我该如何测试 sql 注入呢?
  • 看到sn-ps这么可笑的代码,我一直在想,如果一个网站,它发布的地方,使用了同样的“保护”方法呢?如果这里有这样的检查,你怎么能发布elseif(preg_match('/(and|or|null|not)/i', $str)) 字符串?
  • but its build-in injection prevention functions: quote, quoteInto, quoteIdentifier don't make their work well 哈哈。它适用于所有人。所以,问题出在其他地方。在椅子和显示器之间的某个地方......

标签: php mysql zend-framework sql-injection


【解决方案1】:

我强烈推荐使用Zend_DB。它使用prepared statements
准备好的语句的参数不需要引用;驱动程序会自动处理这个。

如果应用程序专门使用 准备好的语句,开发人员可以 确保不会有 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'];

【讨论】:

  • 数组中的这些参数 - 它们是自动引用还是什么?
  • 是的。这就是准备好的语句的用途。 “准备好的语句的参数不需要引用;驱动程序会自动处理这个。”请阅读链接来源以获取更多信息。
  • 我同意 Benjamin 的观点,使用 Zend_Db 消除了准备好的语句的所有担忧,而且我没有遇到任何问题。
  • 有时准备好的语句也可以允许 SQL 注入:en.wikipedia.org/wiki/…
  • @FractalizeR "如果查询的其他部分是使用非转义输入构建的,SQL 注入仍然是可能的"
【解决方案2】:

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

 $st = $pdo->prepare("SELECT * FROM tbl WHERE x = ?");
 $st->execute($x);

将几个 SQL 语句列入黑名单并不是明智之举。该 Firefox 扩展程序具有误导性,并给您留下了错误的印象。

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

【讨论】:

  • 这正是 Zend_Db 提供的。
  • 我应该如何处理 Zend_Auth_Adapter_DbTable ?
  • 如果使用得当,其实转义是没有问题的
【解决方案3】:

将此代码放入您的标题页中。

function clean_header($string)
{
$string = trim($string);

// From RFC 822: “The field-body may be composed of any ASCII
// characters, except CR or LF.”
if (strpos($string, “\n“) !== false) {
$string = substr($string, 0, strpos($string, “\n“));
}
if (strpos($string, “\r“) !== false) {
$string = substr($string, 0, strpos($string, “\r“));
}

return $string;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多