我正在制作一个Doctrine查询,我必须在where子句中进行通配符匹配。我该如何逃避我想要插入的变量?
我想要的查询:
SELECT u.* FROM User as u WHERE name LIKE %var%
直到现在的PHP代码:
$query = Doctrine_Query::create()
->from('User u')
->where();
where子句应该包含哪些内容?我想要匹配的变量是$ name
答案 0 :(得分:27)
没有人正确回答你的问题,所以我会捅它。
->where('u.name LIKE ?', array("%$name%"));
->where('u.username LIKE ?', '%'.$username.'%')
这些都不安全。让我解释几个场景。
情景1
想象一下,您希望让用户搜索匹配的用户名,但您永远不想列出所有用户名。也许你不希望有人轻易窃取你的一百万个用户名列表。在此代码之前的某个地方,你做了类似这样的事情:
if (strlen(trim($name)) < 5) throw Boogey_Monster_Exception();
您认为这可以防止有人将该字段留空并删除所有用户名列表...但实际上用户可以提交“_____”或“%%%%%”或类似内容以获取列表所有用户名,而不只是匹配5个或更多已知字符。
我个人已经在几个大型公共网站上看到过这种形式的攻击。</ p>
场景2
您的网站拥有大量用户和大量用户数据。您的用户表中有10,000,000行。您希望通过搜索已知的前缀,使网站的用户能够找到其他用户的用户名。
所以你写了一些像这样的代码,从上面的例子稍微修改一下,在搜索字符串之后只有一个通配符。
->where('u.name LIKE ?', array("$name%"));
如果你有一个关于u.name的索引,那么这个LIKE查询将使用索引。因此,如果用户提交$ name =“john”,那么此查询将有效地匹配johndoe,johnwayne,johnwaynegacy等用户。
但是,如果用户提交$ name =“%john”,则此查询不再使用索引,现在需要进行全表扫描。在一个非常大的数据库上,这可能是一个非常慢的查询。
关于SQLi的MySQL手册提到了同样的事情(第78-79页),我搜索了一些慢查询性能的例子并找到了一个链接。
这可能听起来不是什么大问题,但对于由RDBMS支持的站点,RDBMS通常是一个重要的瓶颈,而且大部分性能工程都围绕着减少RDBMS上的争用。如果您有少数用户启动攻击,将数据库句柄占用60秒以上,并且您拥有一小部分数据库句柄,您可以看到它如何快速扩展以独占所有数据库句柄并阻止合法用户能够得到一个。
<强>链接强>
http://dev.mysql.com/tech-resources/articles/guide-to-php-security-ch3.pdf
http://forums.mysql.com/read.php?24,13397,13397
<强>解决方案强>
无论如何,更好的解决方案(如上面链接的MySQL手册和评论者@Maxence所提到的,是使用addcslashes()):
$username = addcslashes("%something_", "%_");
注意,由于这里的sql示例使用的是完全不受sql注入影响的预处理语句,所以不必或不希望使用mysql_real_escape_string();它执行的转义仅仅是为了防止sql注入。我们试图阻止的是通配符注入,这需要一个函数来转义两个sql通配符,'%'和'_'。
答案 1 :(得分:-2)
Doctrine的文档发生了一些不好的事情,所以这里是Google copy(检查 Like Expressions 部分)
...
->where('u.name LIKE ?', array("%$name%"));