如何从根本上防止 SQL 注入?

SQL注入导致的安全问题数不胜数,为什么这么多年来同样的问题一再发生? 如果是因为SQL脚本拼接的原因,为什么不在新的实现中采用api调用的方式来杜绝漏洞? 可能是问题描述不够清晰,再补充一些吧。 楼主本人是懂得如何防止,而且参数化查询这样的方法本身也是很容易掌握的。 自己的疑问是,面对一个很容易解决,又有巨大危害的安全漏洞,靠用户(开发者也是用户)的自觉去避免,这样的机制是不是很不合理? 各大数据库厂商在自…
关注者
1699
被浏览
169753

60 个回答

根本上杜绝的方案太多太简单了,SQL注入这种攻击手段说白了和直接把门踹开偷东西一样Low,这么多年一爆再爆我也很震精。不过想想其实都是一群小白拿着几十年前的小白写的东西改改用才会这样的。

有人非说我没回答问题,OK,根本的手段就是参数化查询或者做词法分析。


@pansz 的答案的确是说到了一个关键,也就是如果支持文本式的指令,那这事儿是没有办法杜绝的,只能在外面套上一个壳,也就是词法分析。


但是现代数据库却不是只提供了SQL文本查询,参数化查询也是标准API之一,当然还有存储过程,这些高级的API都可以有效的杜绝SQL注入这种问题。在MySQL都支持存储过程的今天,还有这么多注入漏洞实在是让人费解。

不使用支持SQL的数据库过于偏颇了,几乎所有的关系型数据库都支持SQL查询,但也提供了安全的查询方式。
SQL注入并不是一个在SQL内不可解决的问题,这种攻击方式的存在也不能完全归咎于SQL这种语言,因为注入的问题而放弃SQL这种方式也是因噎废食。首先先说一个我在其他回答中也曾提到过的观点:没有(运行时)编译,就没有注入

SQL注入产生的原因,和栈溢出、XSS等很多其他的攻击方法类似,就是未经检查或者未经充分检查用户输入数据意外变成了代码被执行。针对于SQL注入,则是用户提交的数据,被数据库系统编译而产生了开发者预期之外的动作。也就是,SQL注入是用户输入的数据,在拼接SQL语句的过程中,超越了数据本身,成为了SQL语句查询逻辑的一部分,然后这样被拼接出来的SQL语句被数据库执行,产生了开发者预期之外的动作。

所以从根本上防止上述类型攻击的手段,还是避免数据变成代码被执行,时刻分清代码和数据的界限。而具体到SQL注入来说,被执行的恶意代码是通过数据库的SQL解释引擎编译得到的,所以只要避免用户输入的数据被数据库系统编译就可以了。

现在的数据库系统都提供SQL语句的预编译(prepare)和查询参数绑定功能,在SQL语句中放置占位符'?',然后将带有占位符的SQL语句传给数据库编译,执行的时候才将用户输入的数据作为执行的参数传给用户。这样的操作不仅使得SQL语句在书写的时候不再需要拼接,看起来也更直接,而且用户输入的数据也没有机会被送到数据库的SQL解释器被编译执行,也不会越权变成代码。

至于为什么这种参数化的查询方式没有作为默认的使用方式,我想除了兼容老系统以外,直接使用SQL确实方便并且也有确定的使用场合。

多说一点,从代码的角度来看,拼接SQL语句的做法也是不恰当的。
DELETE FROM planet WHERE name = 'mercury';
DELETE FROM planet WHERE name = 'venus';
DELETE FROM planet WHERE name = 'earth';
DELETE FROM planet WHERE name = 'mars';
我修改一下那个很经典的笑话:程序员不应该执行删除地球这样的SQL语句,而是写删除一个行星,然后将地球当作参数传入。
$stmt = $mysqli->prepare("DELETE FROM planet WHERE name = ?");
$stmt->bind_param('s', "earth");
$stmt->execute();


更多阅读:
MySQL :: MySQL 5.1 Reference Manual :: 13.5 SQL Syntax for Prepared Statements
Prepared statement