这儿$email是从用户表单中获取来的数据,并且是做为位置参数#1(即第一个问号)传递进来的,因此在任何情况下,这个变量的内容都可以解析为SQL语句。引号、分号、反斜杠、SQL注释表示法-其中的任何一个都不会产生任何特殊的效果,这个因为它们“只是数据”。这不会对其他东西造成破坏,因此这个应用很大程度上防止了SQL注入攻击。
如果对这个查询进行多次重用(这个查询只解析一次)的话,还可以提高性能。然而与获得大量的安全方面的好处相比,这个是微不足道的。这还可能是我们保证互联网应用安全所采取的一个重要的措施。
限制数据库权限和隔离用户
在目前这种情况下,我们观察到只有两个交互式动作不在登录用户的上下文环境中:“登录”和“给我发送密码”。web应用应该使用尽可能少权限的数据库连接:仅对members表具有查询权限,对其它表没有任何访问权限的数据库连接。
这样做的结果是:即便是“成功地”进行了SQL注入攻击,也只能取得非常有限的成功。这种情况下,我们不能做最终授权给我们的任何更新(UPDATE)请求,因此为了能够实现更新(UPDATE)请求,我们不得不寻求其他解决方法。
一旦web应用确定了通过登录表单传递认证凭证是有效的话,那么它将把这个会话切换到一个具有更多权限的数据库连接上。
对任何web应用来说,从不使用sa权限几乎是理所当然的事情。
对数据库的访问采用存储过程
当一个数据库服务器支持存储过程时,请使用存储过程执行这个应用的访问行为,这样(在存储过程编写正确的情况下)就完全不需要SQL了。
通过把诸如查询、更新、删除等某个动作的规则封装成一个单独的存储过程,你就可以根据这个单独的存储过程和所执行的商务规则额对其进行测试和归档。(例如,“增加新的订单”存储过程在客户超过了信用限制的情况下可能拒绝添加这个订单。)
对简单的查询来说,这么做可能仅仅获得很少的好处,不过当这个操作变的越来越复杂(或者是在多个地方使用这个操作)的情况下,给这样操作一个单独的定义就意味着维护这个操作将更简单而且这个操作的功能会更强壮。
注意:总可以编写动态构建查询语句的存储过程:这么做并不会防止SQL注入-它只不过把准备/执行过程正确地结合在一起,或者只不过把SQL语句与提供保护的变量直接捆绑在一起。
隔离web服务器
即便已经实施了所有这些预防措施,仍然有可能遗漏了某些地方,从而使服务器对于伤害而门户洞开。你应当在设计网络基础架构的时候假设坏人获得了完全访问机器的管理员权限,然后试图降低这种情形对其它事情伤害的影响。
例如,把这台机器放置在网络“内部”具有非常少漏洞的DMZ区,这么做意味着即便取得了web服务器的完全控制也不能自动的获得对其他一切的完全访问权限。当然,这么做不能阻止所有的入侵,不过它可以使入侵变的非常困难。
配置错误汇报机制
一些框架的默认错误汇报机制包括开发人员的调试信息,而且这不可能向外部用户公开。想象一下:如果完整的查询都显示出来,并且知名语法错误的地方,那么对攻击者来说,这时的攻击将是多么容易啊。
这些信息对开发者来说很有用,不过如果可能的话,应当限制只有内部用户才能访问。
注意:不是所有的数据库都可以采用同样的方式配置,而且并不是所有的数据库都支持相同的SQL(这里“S”代表的是“结构化的”,而不是“标准的”)方言。例如,MySQL的大多数版本都不支持子查询,同时也不允许出现多个语句:当你试图穿透一个网络的时候,这些实质上就是使问题复杂化的因素。