使用 PDO 的 prepare 预处理,能 100%防止 SQL 注入吗?

2017-05-20 15:23:11 +08:00
 xiaoyanbot

手册上说:

提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理。如果应用程序只使用预处理语句,可以确保不会发生 SQL 注入。(然而,如果查询的其他部分是由未转义的输入来构建的,则仍存在 SQL 注入的风险)。

查询的其它部分不用输入。 是不是可以确保 100% 不会 SQL 注入了呢 ?

10794 次点击
所在节点    PHP
59 条回复
leeg810312
2017-05-21 08:04:00 +08:00
一个说好好写代码,避免已知 bug,肯定不会有 sql 注入。一个说总会有写不好代码的时候,所以一定不能避免 sql 注入。就 lz 问题,我认为前者的回答是没有问题的,后者有点吹毛求疵。
bianhua
2017-05-21 09:01:24 +08:00
@leeg810312 如果你在说我吹毛求疵的话,那么你真应该看看我到底说了什么:
https://www.v2ex.com/t/362646#r_4343233

当然,大部分人都喜欢简单的答案嘛。“能”,够简单,喜欢吧?

可你在发帖的时候也带了前提:“好好写代码,避免已知 bug ”。

这跟我说的难道不一样?
yangqi
2017-05-21 10:52:00 +08:00
@bianhua 要么是你逻辑有问题,要么是你语言表达有问题。打个比方,你问别人“用枪能 100%杀死一个人么?”,答案很明显是能。但你说不能,然后举了个例子,开枪的人不会使用或者打偏了,所以得出结论,枪不能 100%杀死人,还要看用枪的人。乍一看好像有点道理,其实是典型的答非所问,或者说利用问题里没有提到的条件来诡辩。
ovear
2017-05-21 11:01:30 +08:00
@bianhua 不好意思,你不能正确讨论问题,导致你已经被降权了。不是 @msg7086 我还收不到提示。

@msg7086 所以这种情况已经排除掉了,要是这种可能性算上去,那任何事件都不可能 100%了。硬件还可以受到辐射导致 1+1 != 2 呢。

至于我做好什么工作,那我的确没有做功课,我全程都是一边看 银河护卫队 一边回答你的。但是到后来,中断的次数已经有点让我不开心了。
我的目的是纠正你对 prepare 错误的理解,然而你对我的一直是人身攻击。我也不好说什么了。

另外可能你的语文理解能力也有点问题。
我一直在说的是
你使用了不能 native prepare 的语句
你使用了不能 native prepare 的语句
你使用了不能 native prepare 的语句

从而导致了
pdo 回退为拼接 sql
pdo 回退为拼接 sql
pdo 回退为拼接 sql

够清楚了吧,我不很不懂为什么讨论一个技术问题,要上升到人身攻击上来。
而且我是很不懂为什么我跟你说 A,你要跟我说 B,我说你 B 说的有问题,你又跟我说 C。这样偷梁换柱完全没意思啊。
整天 judge 别人,噢你很正义,你很邪恶,没有什么意思啊。
我也没看出什么最后一根稻草,你说了半天只能佐证你之前的英语不好啊,理解错了而已。

PS: 这位层主上面的理论还是全部错误的,至少关于 MySQL native prepare 是这样。

Are PDO prepared statements sufficient to prevent SQL injection?
https://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection/12202218#12202218
这个帖子的层主引用的是他在

https://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string/12118602#12118602
SQL injection that gets around mysql_real_escape_string()

的回复。

从这里可以看出,这其实是 mysql_real_escape_string 的一个 bug 吧,PDO boom 的原因,是因为他 fallback 到普通的 sql query 模式了。

我也引用一下这位 stackoverflow 答主的回答吧

Wrapping Up

If you:

Use Modern Versions of MySQL (late 5.1, all 5.5, 5.6, etc) AND PDO's DSN charset parameter (in PHP ≥ 5.3.6)
OR

Don't use a vulnerable character set for connection encoding (you only use utf8 / latin1 / ascii / etc)
OR

Enable NO_BACKSLASH_ESCAPES SQL mode
You're 100% safe.

这个 100% safe 是通常意义上的 100%,是可知论中的 100%。

至此,我再参与与 @bianhua 的任何讨论,被人身攻击我已经很不开心了,我也不能保证再继续讨论,自己是否能理性分析问题,不跑题了。

当然如果我有错误,或者各位有任何问题都可以 @我。
bianhua
2017-05-21 11:05:04 +08:00
@yangqi

不,我不觉得我有任何问题。因为(可能很多人看贴的时候忽略了这一点)其中的一个陈述:100%

PDO prepare 能防止 SQL 注入么?能
PDO prepare 能 100%防止 SQL 注入么?不能,除非你正确的使用了它

用枪能杀死一个人么?能
用枪能 100%杀死一个人么?不能,除非你正确的使用了它

不知道你是否同意上面的陈述。

// BTW: Dear FBI & NSA, yes, I just googled something like "Can I 100%ly kill a man with gun" on ... Google. It's just for this topic, I'm not actually going to kill anyone, please don't come to my door. Thank you!
bianhua
2017-05-21 11:08:50 +08:00
好了,我早就开始后悔回复这些帖子了。

如果各位路过的看客在看过所有相关的内容之后仍然认为我是在“诡辩”,请麻烦按下 Block。

(如果你不愿意看相关的内容,也请 Block )
yangqi
2017-05-21 11:42:43 +08:00
@bianhua 呵呵,你这么说就说明你逻辑问题了。或者是语文没学好。如果要问,“有了枪能 100%杀死一个人么?”和“用枪能 100%杀死一个人?”这是两个不同的问题。同理你想问的问题是“用了 PDO 的 prepare 处理,是不是 100%能防注入”?,这个和你问的“ PDO 的 prepare 处理是否能 100%防注入?”表达的是不同意思好吗?你小学语文应该没学好吧?
bianhua
2017-05-21 11:58:34 +08:00
@yangqi

可能吧,我并没有看出

> “用了 PDO 的 prepare 处理,是不是 100%能防注入(?)”



> “ PDO 的 prepare 处理是否能 100%防注入?”

在这个问题上的区别。

感谢你一直和我讨论这个问题,现在我们能让它过去了么?我实在不想继续回复这个帖子。事实上,我想我应该不太会想其他的帖子了。不是厌恶,只是为了避免由于我的理解不好而引起不必要的争论。
yangqi
2017-05-21 12:05:10 +08:00
@bianhua 呵呵,所以问题归根结底是你小学语文没学好,想问的问题和表达的不是一个意思,导致交流不在一个频率上。
bianhua
2017-05-21 12:23:02 +08:00
@yangqi 嗯,是我的错。

但是,自从回了这个帖子之后,其他的回帖者在总结这个问题的时候,都会带上“在正确使用的前提下”这个提示。

就像我很早之前就说到的:
> 安全问题没有银弹,不要认为引入一个库或者一个方法就能解决所有问题,你还需要知道如何正确的使用它们。
>
> 综上所述,安全是个体系,而不是一条函数。你需要将所有的事情作对才是安全的。

这逆转了一开始两个盲目信心满满的“能”,我对这样的结果是满意的,只是我在这件事上并没有得到任何好处。
bianhua
2017-05-21 13:14:05 +08:00
@ovear

我并不在乎一个网站的帐号,但如果我让你觉得我在人身攻击你,我向你道歉。至于我 AT 你你无法收到,除了降权,可能还因为我 Block 了你。

我这么做是因为想要控制我能跟你争辩的频率,这样我不会因为太过生气。

生气的原因是这样的:

我给出的链接:
stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection/12202218#12202218

The Attack 部分讲述的是攻击的过程和方法,其实就是这个版本:
shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string

主题是:addslashes() versus mysql_real_escape_string() debate。就是说在过滤数据的时候是否应该用 addslashes (这不是犯傻么?)。Blog 仍然是拿 0xbf27 来举例,字符变形到 0xbf5c27 然后进行攻击,很简单,几十年前的技术。

结论是:To avoid this type of vulnerability, use mysql_real_escape_string(), prepared statements, or any of the major database abstraction libraries. 就是说通过 mysql_real_escape_string 来避免注入,而不要用 addslashes。

于是 SO 上的回答就着这个问题衍生到了 PDO 上,然后故意没有 $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 这样当然会退回本地的模拟。

这是那个 SO 帖子的主题。下面的 Selecting a Character Set、The Payload、$stmt->execute()和 The Query 都是在讨论这个问题。这些都是进行在 MySQL 上的,而不是本地驱动。

而解决方案则列在了 The Simple Fix、The Correct Fix 和 The Saving Grace 这几个小结下,其中 The Simple Fix 小节里,作为一种提示,SO 的 PO 主提到:However, be aware that PDO will silently fallback to emulating statements that MySQL can't prepare natively: those that it can are listed in the manual, but beware to select the appropriate server version)。告诉你即使$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);之后,仍然有可能退回到本地 prepare,并且给出了档案

最后,Safe Examples、Wrapping Up 和 Addendum 对 SO 回答进行了总结。

这就是为什么我一开始觉得是不是我英语不好看漏了某些部分,因为我觉得能挑出那句话放在不应该的地方很奇怪。然而当我看过了全部链接并且提示你之后,你仍然在向我抛出“ However, be aware that PDO will silently fallback to emulating statements that MySQL can't prepare natively ”这句话,并且指责是我没有认真了看过文章并且了解原理。

我个人觉得这是对我之前所花费精力的冒犯。

当然,可能我理解错了一部分你的发言。如果是这样,非常抱歉。
sagaxu
2017-05-21 13:51:43 +08:00
如果是我用,可以 100%保证
styx
2017-05-21 13:56:40 +08:00
@bianhua @ovear 我看完整个讨论,觉得前半部分讨论已经把问题的大部分都讲明白了,只是你们执着于不同的攻击层面不愿把观点结合起来。我理解下来 @ovear 的意思是在 MySQL 的接口层面(即抓包所表示的 API ),可以认为 native prepare 是不会引入注入攻击的,因为 MySQL 是 blabla 这样做的。而 @bianhua 的重点则在于 PHP 如果认为后端 MySQL 无法 native prepare,那么会在 PHP 中自己做拼接而不能正确调用 native prepare (个人猜测如果强行 native prepare 的话应该是直接返回错误),导致可能注入。可以看到在 MySQL (native?) <--> PHP 的中间,两人的讨论重点错开来了。

其实如果不看 SO 最开始的提问,不纠结谁对错,都退一步的话,大概就可以整合得出一个完整的回答吧,好像就是 SO 那个回答。
lsido
2017-05-21 16:14:52 +08:00
那么这个问题,没有继续下去的必要了,楼主的意思我可以这样理解然后回答:无论是 pdo 还是拼接,只要在 [正确的情况下] ,都可以 100%避免
ywisax
2017-05-21 16:41:26 +08:00
就我的 code review 经验来看,pdo 参数绑定是足够安全的了。
不过实际项目中使用了 pdo 也依然会有被注入的危险,因为 prepare 之前依然有 sql 拼接的过程存在,这个环节处理不好依然会被带入恶意字符。
voocel
2017-05-21 17:26:17 +08:00
到此为止!
Silicon
2017-05-22 05:24:51 +08:00
我建议 @bianhua 如果你真的想证明有问题的话,毋须多言,丢以下信息上来:
* 页面代码
* Nginx/Apache、PHP 和 MySQL 版本
* PoC

这样无论是 @ovear 还是其他人,都有一个讨论的原点。

手册和 stackoverflow 都是没什么意义的,PoC 和 exp 才有意义。
msg7086
2017-05-22 09:46:06 +08:00
@bianhua
@ovear
所以我说你们俩就没站在一个层面上啊。

@bianhua 你这里提到的:
PDO prepare 能 100%防止 SQL 注入么?不能,除非你正确的使用了它

根据你自己的前提来看,你这个结论当然也是错的。
不仅仅需要你正确使用它,还需要类库的作者正确处理了他,还需要服务器端正确处理了他,还需要服务器硬件正常运行而没有受到大量电磁干扰或者辐射,等等。

大部分情况下我们讨论内容需要有一个共同的基准,这个基准就是 Common Sense 常理。如果你对常理之外的东西进行补充,这当然没问题,但是你揪着一个常理外的做法去钻牛角尖就不好了。

就说你 4 楼的回复,如果换成「核弹爆炸造成严重电磁干扰会影响 MySQL 的语法解析器造成注入漏洞」,那你一眼就能看出这是句意义不大的回复。那么同样的「绕过标准的 set_charset 函数,故意为服务器单独设定不同的字符集并且使用不能 native prepare 的语句去触发内置的 escape 再故意填入字节编码中含有引号的字符串会造成注入漏洞」,对正常讨论串来说也是意义不大的回复。你可以把他当做补充说明,但是开口就是一句「感觉你们信心很足啊。」怼上来,就不太好看了吧。

如果你只是根据 100%这个词来辩的话,那就更没意思了,因为严格来说「 100%」本身就没有意义,就像「无限」这个词一样。
xiaoyanbot
2017-05-22 23:27:30 +08:00
感谢各路大神回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/362646

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX