没有人正确回答你的问题,所以我来试着解答一下。
->where('u.name LIKE ?', array("%$name%"));
->where('u.username LIKE ?', '%'.$username.'%')
这两种方法都不安全。让我解释一下几种情况。
情况1
假设您想让用户搜索匹配的用户名,但从未想过要列出所有的用户名。或许您不希望有人轻易地从您那里窃取一百万个用户名列表。在此代码之前,您可能已经做了类似于以下的操作:
if (strlen(trim($name)) < 5) throw Boogey_Monster_Exception();
你认为这样可以防止用户留空并拉下所有用户名的列表,但实际上用户可以提交"_____"或"%%%%%"或类似的内容来获取所有用户名的列表,而不仅仅是匹配5个或更多已知字符的用户名。
我亲眼见过这种攻击方式在一些大型公共网站上使用。
场景2
你有一个拥有大量用户和用户数据的网站。你的用户表中有1000万行数据。你想让网站的用户通过搜索已知前缀来查找另一个用户的用户名。
因此,你编写了以下代码,稍作修改以仅在搜索字符串之后具有通配符。
->where('u.name LIKE ?', array("$name%"));
如果您在u.name上有一个索引,那么这个LIKE查询将使用该索引。因此,如果用户提交$name="john",则此查询将有效匹配像johndoe、johnwayne、johnwaynegacy等用户。
但是,如果用户提交$name="%john",则此查询不再使用索引,现在需要进行全表扫描。在非常大的数据库上,这可能是一个非常缓慢的查询。
MySQL关于SQLi的手册也提到了这一点(第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通配符字符:%和_。
addcslashes('%something_', '\\%_');
。请记住,在MySQL中,'\\something' LIKE '\\something'
评估为FALSE
,但'\\something' LIKE '\\\\something'
评估为TRUE
;-) - KarolisLIKE ...
条件的转义字符,否则不会使用C样式转义。(请参见https://www.sqlite.org/lang_expr.html中的“字面值”部分)。不幸的是,我不知道如何使Doctrine的查询构建器工作(我只使用DBAL),但该概念在这里有解释:https://dev59.com/aWw05IYBdhLWcg3wTwGY#7323498。 - Jordan Lev