这是一篇翻译的文章,可以点击查看原文。

简单来说不能,PDO prepare无法防御全部的SQL注入攻击。比如一些潜在的边缘情况。

我调整了这个答案以适应PDO的场景……

完整的答案是不太容易,这篇文章证明了这种攻击的可能。

攻击实现

那么,接下来我来演示这种攻击……

$pdo->query('SET NAMES gbk');

$var = "\xbf\x27 OR 1=1 /*";

$query = 'SELECT * FROM test WHERE name = ? LIMIT 1';

$stmt = $pdo->prepare($query);

$stmt->execute(array($var));

某些情况下,上面的代码会返回多于一行数据。让我们剖析一下这里到底发生了什么:

1. 选择一个编码集

$pdo->query('SET NAMES gbk');

要让这种攻击生效,我们需要服务编码和连接编码两者都把'以ASCII形式编码,也就是0x27,并且这种编码中要存在某个字符以ASCII的\也就是0x5c结尾。事实上,MySQL5.6默认支持的编码中有五种符合这一要求:big5、cp932、gb2312、gbk还有sjis。我们这里用gbk举例。

现在,请格外注意这里的SET NAMES指令,它改变了服务器上的字符集。当然还有另一种方法修改字符集,我们后面会谈到。

2. 要嵌入的字符串

我们用于演示SQL注入的字符串以0xbf27开头。在gbk编码中,这是个不合法的多字节字符,而在latin1编码中,它是字符串¿'。注意,不论latin1还是gbk编码,0x27都是半角单引号'字符。

我们选择这个字串的原因在于,如果我们对它调用addslashes(),我们会在'字符前插入一个ASCII码的\字符,即0x5c。然后我们得到字符串编码是0xbf5c27,在gbk编码中,这是两个字符0xbf5c,后面接着0x27。换句话说,这是一个合法的字符后面跟着一个没转义的'。不过我们没调用addslashes(),那么会进行下一步。

3. $stmt->execute()

这里有一件重要的事情需要搞清楚的是,PDO在默认情况下并不会真的对语句做prepare,而是会模拟这一行为(对于MySQL)。也就是说,PDO会在内部构建查询语句,对每个绑定的字符串调用mysql_real_escape_string()(MySQL的C语言API中的函数)。

C语言API调用mysql_real_escape_string()和addslashes()的不同点在于前者知道MySQL连接的字符集,所以它可以正确地根据服务器要使用的字符集进行转义。然而,正因为这一点,MySQL客户端会使用latin1进行转义——因为我们从来没有通知它切换字符集的事情。我们的确告诉服务器我们在用gbk,可是客户端仍以为是latin1。

这样的情况下调用mysql_real_escape_string()时会插入反斜杠\,而那个挂单的'就会被转义出来!实际上,如果我们以gbk编码查看$var的值是:

縗' OR 1=1 /*

以上就是进行攻击的环境要求。

4. 生成的查询语句

这部分只是为了完整,下面是生成的SQL查询语句:

SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

恭喜,你刚刚完成了一次针对PDO prepare程序的攻击。

简单的修复方法

值得注意的是你可以简单地通过禁用prepare语句模拟功能来防御这种攻击:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

这样一般导致PDO执行真正的prepare操作。(数据会独立于SQL语句传送给服务器。)不过还是得小心PDO会在本地的MySQL无法完成prepare时悄悄地恢复使用模拟prepare语句的状态。具体情况在MySQL手册中已然列出,请注意选择对于你服务器版本的手册。

正确的修复方法

这里真正的问题在于我们没有调用MySQL的C语言API中的mysql_set_charset(),而是使用了SET NAMES语句。如果正确调用了mysql_set_charset(),那么使用2006年之后发布的MySQL版本都将安全。

如果你仍在使用更早版本的MySQL,那么mysql_real_escape_string()中的一个bug会导致它把类似我们例子中的不合法的多字节字符串当作单字节字符串加以处理,这样即便客户端正确地指定了连接编码集和服务器编码集,仍会存在被攻击的风险。这个bug在MySQL 4.1.20、5.0.22和5.1.11中得以修复。

不过最糟的问题在于PHP 5.3.6之前版本的PDO压根就没暴露C语言API中的mysql_set_charset()方法,所以早于此版本的用户根本不可能阻止这种类型的攻击。现在PDO以一个DSN参数的形式暴露了这个接口,应该用这种方法取代SET NAMES……

其它好办法

话说回来,这种攻击能生效的前提条件是数据库连接使用了薄弱的字符编码。utf8mb4是一种不存在此类弱点的字符集编码方法,而且它能支持所有的Unicode字符——你应该考虑切换到这种编码上。可惜只有MySQL 5.5.3以上版本支持这一编码。另一个可以考虑的替代字符集编码是utf8,它也不存在前述弱点,而且能支持所有Unicode基本多文种平面中的字符

此外,你也可以启用NO_BACKSLASH_ESCAPES SQL模式,这会改变mysql_real_escape_string()的内部行为(也包括其它东西)。当这个模式启动时,0x27会被替换为0x2727而不是0x5c27,这样转义后输出的字符在所有的薄弱的编码字符集中都不会和前面的字符连成合法的存在(比如0xbf27转义出来还是0xbf27),最终服务器就会拒绝这个非法的字符串。不过请看看@eggyal的回答以了解这种SQL模式下的别的漏洞(尽管与PDO无关)。

安全的例子

以下例子是安全的:

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 /*"));

因为关掉了prepare模拟。

$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总是执行真正的prepare操作。

总结一下

如果你:

使用了现代版本的MySQL(晚期版本的5.1,所有的5.5,所有的5.6等等)并且使用了PDO的DSN里的charset参数(PHP ≥ 5.3.6)

或者

没用有弱点的连接字符集编码(比如只使用utf8、latin1、ascii等等)

或者

启用了NO_BACKSLASH_ESCAPES SQL模式

那么你100%安全。

其它情况下,即使你用了PDO的prepare方法也依然存在SQL注入风险。

补充

我正在慢慢编写一个补丁程序让未来版本的PHP默认不要使用模拟prepare方法。导致我进展缓慢的原因在于实在太多测试阻止我这么做。其中一个问题是模拟prepare功能只会在执行的时候返回SQL语法错误,然而真正的prepare可能会在进行prepare的时候就回报错误,这会带来许多麻烦(这也是导致那些测试出错的部分原因)。

mysql prepare 注入_PDO prepare足以防止SQL注入吗?相关推荐

  1. mysql sql注入很常用_常见sql注入的类型

    这里只讲解sql注入漏洞的基本类型,代码分析将放在另外一篇帖子讲解 目录 最基础的注入-union注入攻击 Boolean注入攻击-布尔盲注 报错注入攻击 时间注入攻击-时间盲注 堆叠查询注入攻击 二 ...

  2. sql 整改措施 注入_记一次Sql注入 解决方案

    老大反馈代码里面存在sql注入,这个漏洞会导致系统遭受攻击,定位到对应的代码,如下图所示 image like 进行了一个字符串拼接,正常的情况下,前端传一个 cxk 过来,那么执行的sql就是 se ...

  3. SQL注入:SQL注入类型(手动)SQL注入的检测

    一.SQL注入流程 1.判断是否有SQL注入漏洞(检测) 2.判断操作系统.数据库和web应用类型 3.获取数据库信息,包括管理员信息及拖库 4.加密信息破解,sqlmap可自动破解 5.提升权限,获 ...

  4. sql注入攻击的原理(sql注入攻击防范)

    SQL 注入(SQLi)是一种可执行恶意 SQL 语句的注入攻击.这些 SQL 语句可控制网站背后的数据库服务.攻击者可利用 SQL 漏洞绕过网站已有的安全措施.他们可绕过网站的身份认证和授权并访问整 ...

  5. sql注入漏洞,应屏蔽SQL注入攻击

    2019独角兽企业重金招聘Python工程师标准>>> 注:SQL注入好比是前端URL传参数请求时参数以SQL 做为参数传入,如 select 1  from dual where ...

  6. Mybatis的SQL注入隐患操作复现防止SQL注入

    很多人都知道SQL存在SQL注入引起的安全问题,但是很少有开发者去尝试过是怎样一个注入,下面将复现Mybatis的SQL注入问题并给出正确操作. 1.复现SQL注入操作 1.新建一张表(MqSQL): ...

  7. php mysql 注入漏洞_PHP安全:SQL注入漏洞防护

    原标题:PHP安全:SQL注入漏洞防护 SQL注入是最危险的漏洞之一,但也是最好防护的漏洞之一.本文介绍在PHP的编码中合理地使用MySQL提供的预编译进行SQL注入防护,在PHP中使用PHP数据对象 ...

  8. mysql 主键 uniqo_优衣库某处SQL注入可导致移动平台被劫持

    漏洞概要 缺陷编号:WooYun-2014-073739 漏洞标题:优衣库某处SQL注入可导致移动平台被劫持 相关厂商:优衣库 漏洞作者:winsyk 提交时间:2014-08-25 10:08 公开 ...

  9. 绕过waf mysql爆库_iwebsec刷题记录-SQL注入漏洞

    被推荐了这个web平台,感觉挺适合新手的,网上搜了下没看到有人写wp,这里本入门萌新来稍微整理下自己解题的过程 SQL注入漏洞 01-数字型注入 http://localhost:32774/sqli ...

最新文章

  1. Linux 命令 —— scp linux之间复制文件和目录
  2. python menu实例_Python高级进阶#019 pyqt5菜单menu应用,新建多窗体
  3. outofmemory异常如何解决?
  4. php有关卡的消消乐源码,查看“宝可消消乐/活动关卡/得分挑战关卡/超梦”的源代码...
  5. 51Nod 1256 乘法逆元 Label:exgcd
  6. 苹果mac视频和字幕下载软件:Downie
  7. 学校计算机协会有哪些部门,大学生计算机协会部门职能划分(7页)-原创力文档...
  8. CSSG:Cobalt Strike Shellcode生成工具
  9. 王道训练营Day24——Linked
  10. 【c++算法】《c/c++实现SM4加密解密算法》
  11. cad.net 更改高版本填充交互方式为低版本样子
  12. 如何成功将qlv格式转换成mp4,方法免费简单好用
  13. 【破解软件】知音漫客免费看更多漫画
  14. 通过rgb值判断颜色深浅
  15. 微信保存图片查看与清理工具
  16. 我的第一款(ban)产(cheng)品(pin)——铛铛打卡
  17. 第031讲:永久存储:腌制一缸美味的泡菜
  18. matlab 格兰杰,matlab非参数的格兰杰因果分析
  19. 中标麒麟桌面版系统(V7.0)安装教程
  20. 磁盘管理大师Paragon Hard Disk Manager for mac永久试用版

热门文章

  1. 一级计算机赵州桥,一级计算机练习题计算机一级计算机基础及+MS+OFFICE+应用(操作题)-试卷8...
  2. 《脱颖而出——成功网店经营之道》一2.3 拉旗号——品牌
  3. docker desktop如何清理redis缓存(mac)
  4. 区块链+物联网设备,能产生什么反应?
  5. border-box和content-box的区别和解释
  6. 没错,AR属于AI:A Tale of Two A's
  7. 电脑中 计算机 无法打开,电脑上所有exe文件都打不开怎么办?
  8. java 背单词系统_java实现背单词程序
  9. php file_put_contents 换行追加,PHP中file_put_contents追加和换行的实现方法
  10. 合作伙伴收入增长11倍,腾讯云的野心不止10朵云