3

我最近在向我的朋友解释参数化及其优点,他问它比mysqli_escape_string安全性更好。具体来说,您能想到任何 SQL 注入示例,尽管输入字符串被转义(使用 mysqli_escape_string),但仍会成功?

更新:

对于我最初的问题不够清楚,我深表歉意。这里要问的一般问题是,尽管转义了输入字符串,SQL 注入是否可能?

4

2 回答 2

4

更新的答案

该问题已被编辑(在我的答案发布后)专门针对 target mysqli_escape_string,这是一个别名,mysql_real_escape_string因此考虑了连接编码。这使得原始答案不再适用,但为了完整起见,我将其保留。

简而言之,新答案在mysqli_escape_string安全方面与参数化查询一样好,前提是您不要误伤自己

具体来说,PHP文档页面上的巨大警告中突出显示了您不能做的事情:

字符集必须在服务器级别设置,或者使用 API 函数mysqli_set_charset()设置才能影响 mysqli_real_escape_string().

如果您不注意此警告(即,如果您使用直接SET NAMES查询更改字符集)并且将字符集从单字节编码更改为“方便”(从攻击者的角度来看)多字节编码,您将拥有实际上模仿了哑巴mysql_escape_string的做法:在不知道输入的编码的情况下尝试转义字符。

这种情况使您可能容易受到 SQL 注入的攻击,如下面的原始答案所述。

重要提示:我记得在某处读到最近的 MySql 版本已经在其末端(在客户端库中?)堵住了这个漏洞,这意味着即使使用切换到易受攻击的多字节编码,您也可能是完全安全的。SET NAMES但请不要相信我的话。

原始答案

相比之下mysql_real_escape_string,baremysql_escape_string不考虑连接编码。这意味着它假设输入采用单字节编码,而实际上它可以合法地采用多字节编码。

一些多字节编码具有对应于单个字符的字节序列,其中一个字节是单引号 ( 0x27) 的 ASCII 值;如果输入这样一个字符串,mysql_escape_string将很高兴地“转义引号”,这意味着0x270x5c 0x27. 根据编码规则,这可能会导致将多字节字符变为另一个包含 的字符,0x5c 并将“剩余”0x27作为独立的单引号留在 input 中。瞧,您在 SQL 中注入了一个未转义的引号。

有关更多详细信息,请参阅此博客文章

于 2012-10-23T08:47:37.603 回答
4

不好的答案:

具体来说,您能想到任何 SQL 注入示例,尽管输入字符串被转义(使用 mysql_escape_string),但仍会成功?

不可靠。您指的是mysql_escape_string(),它没有考虑连接编码(虽然mysql_real_escape_string())。

因此,也许一个精心制作的字符串,前面有一个精心制作的不完整 UTF8 代码点,可能会导致,比如说,引号被转义,mysql_escape_string() 但转义本身被 MySQL 忽略,因为它会将其“视为”为 UTF8 字符。

例如:

0xC2' OR 1=1 ;--

会被mysql_escape_string()as逃脱

0xC2\' OR 1=1 ;--

这将被组装到

WHERE password='0xC2\' OR 1=1 ;--';

并被 MySQL 视为(如果正确的连接编码有效),例如,

WHERE password='€' OR 1=1 ;[--';]    <-- the bracketed part is considered a comment and ignored

这将是一个经典的 SQL 注入。

但这取决于您指定的事实,也许是通过分心,一个双重弃用的功能。如果你真的指的是mysql_real_escape_string(),那么它就行不通了。

此外,这假定服务器和应用程序层(例如 PHP)在填充输入时都没有使用任何类型的字符集验证。如果他们这样做了,无效的 UTF8 将在到达时被删除,甚至永远不会被 看到mysql_escape_string,这当然就足够了。

真正的答案:

根本不要使用mysql_escape_string(或mysql_whatever)。它们已被弃用,您的代码可能会停止工作。请改用 PDO 函数。

于 2012-10-23T08:58:53.150 回答