mysql real escape_围绕mysql_real_escape_string()的SQL注入

简短的回答是肯定的,是的,有办法绕行mysql_real_escape_string()。

对于非常糟糕的边缘案例!!!

答案很长并不容易。它基于此处演示的攻击。

攻击

那么,让我们从展示攻击开始......mysql_query('SET NAMES gbk');$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

在某些情况下,这将返回超过1行。让我们剖析一下这里发生了什么:选择一个字符集mysql_query('SET NAMES gbk');

为了使这种攻击起作用,我们需要服务器在连接上期望的编码既可以'用ASCII 进行编码,0x27也可以有一些字符的最终字节是ASCII \即0x5c。事实证明,会默认在MySQL 5.6支持5个这样的编码:big5,cp932,gb2312,gbk和sjis。我们会gbk在这里选择。

现在,注意SET NAMES这里的使用非常重要。这会将字符集设置为“服务器”。如果我们使用了对C API函数的调用,那么mysql_set_charset()我们就可以了(自2006年以来的MySQL版本)。但更多的是为什么在一分钟......

有效载荷

我们将用于此注入的有效负载从字节序列开始0xbf27。在gbk,这是一个无效的多字节字符; 在latin1,它是字符串¿'。请注意,在latin1 和中 gbk,0x27它本身就是一个文字'字符。

我们选择了这个有效载荷,因为,如果我们叫addslashes()上他,我们就插入一个ASCII \即0x5c,在之前'的字符。所以我们结束了0xbf5c27,这gbk是一个两个字符的序列:0xbf5c接下来0x27。或者换句话说,一个有效的字符后跟一个未转义的字符'。但我们没有使用addslashes()。那么下一步......

mysql_real_escape_string()

C API调用的mysql_real_escape_string()不同之处addslashes()在于它知道连接字符集。因此它可以为服务器期望的字符集正确执行转义。然而,到目前为止,客户认为我们仍在使用latin1连接,因为我们从未告诉过它。我们告诉了我们正在使用的服务器gbk,但客户端仍然认为它是latin1。

因此调用mysql_real_escape_string()插入反斜杠,我们'在“转义”内容中有一个自由悬挂的字符!事实上,如果我们看一下$var在gbk字符集,我们会看到:缞'或1 = 1 / *

这正是攻击所需要的。

查询

这部分只是一种形式,但这里是渲染的查询:SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

恭喜,您刚刚使用mysql_real_escape_string()... 成功攻击了一个程序

它变得更糟。PDO默认使用MySQL 模拟预准备语句。这意味着在客户端,它基本上执行sprintf mysql_real_escape_string()(在C库中),这意味着以下将导致成功注入:$pdo->query('SET NAMES gbk');$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

现在,值得注意的是,您可以通过禁用模拟的预准备语句来防止这种情况:$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

这通常会导致真正准备好的语句(即数据在查询的单独数据包中发送)。但是,请注意,PDO将无声地回退到模拟MySQL本身无法准备的语句:可以在手册中列出的那些语句,但要注意选择适当的服务器版本)。

丑陋的

我在一开始就说过,如果我们使用mysql_set_charset('gbk')而不是,我们可以阻止所有这些SET NAMES gbk。如果您从2006年开始使用MySQL版本,那就是这样。

如果您使用的是较早的MySQL版本,那么错误的mysql_real_escape_string()意思是无效的多字节字符,例如那些在我们的有效载荷被视为单字节转义的目的,即使客户端已正确通知连接编码的,因此这种攻击仍然成功。该错误是固定在MySQL 4.1.20,5.0.22和5.1.11。

但最糟糕的是,直到5.3.6 PDO才公开C API mysql_set_charset(),因此在以前的版本中,它无法阻止每次可能命令的攻击!它现在作为DSN参数公开。

拯救恩典

正如我们在开始时所说的,为了使这种攻击起作用,必须使用易受攻击的字符集对数据库连接进行编码。 utf8mb4是不容易,但可以支持所有的 Unicode字符:所以你可以选择使用的是代替,但它只是从MySQL 5.5.3可用。另一种选择是utf8,它也不易受攻击,可以支持整个Unicode 基本多语言平面。

或者,您可以启用NO_BACKSLASH_ESCAPESSQL模式,其中(除其他外)改变了操作mysql_real_escape_string()。启用此模式后,0x27将替换为0x2727而不是0x5c27因为转义进程无法在以前不存在的任何易受攻击的编码中创建有效字符(即0xbf27仍然是0xbf27等等) - 因此服务器仍将拒绝该字符串为无效。但是,请参阅@ eggyal的答案,了解使用此SQL模式可能产生的其他漏洞。

安全的例子

以下示例是安全的:mysql_query('SET NAMES utf8');$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");mysql_query("SELECT * FROM test WHERE name = '$var'

LIMIT 1");

因为服务器期待utf8......mysql_set_charset('gbk');$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

因为我们已正确设置字符集,因此客户端和服务器匹配。$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);$pdo->query('SET NAMES gbk');$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ?

LIMIT 1');$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

因为我们已经关闭了模拟准备好的语句。$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ?

LIMIT 1');$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

因为我们已经正确设置了字符集。$mysqli->query('SET NAMES gbk');$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');$param = "\xbf\x27 OR 1=1 /*";$stmt->

bind_param('s', $param);$stmt->execute();

因为MySQLi一直都做真实的准备语句。

包起来

如果你:使用MySQL的现代版本(5.1之后,全部5.5,5.6等)和 mysql_set_charset() / $mysqli->set_charset()/ PDO的DSN字符集参数(在PHP≥5.3.6中)

要么不要使用易受攻击的字符集进行连接编码(仅使用utf8/ latin1/ ascii/ etc)

你100%安全。

否则,即使你正在使用mysql_real_escape_string(),你也很脆弱......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值