如果是的话,为什么还有那么多成功的SQL注入攻击呢?只是因为一些开发人员没有使用参数化语句吗?
如果是的话,为什么还有那么多成功的SQL注入攻击呢?只是因为一些开发人员没有使用参数化语句吗?
当文章谈论参数化查询可以防止SQL攻击时,他们通常不解释为什么,往往是“能防止,所以不要问为什么”——可能是因为他们自己也不知道。一个坏的教育者的确准确的表现就是无法承认自己不知道某些东西。但我离题了。 当我说我觉得感到困惑是很容易理解的时候,我的意思很简单。想象一下动态SQL查询。
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
一个简单的SQL注入攻击就是将用户名输入为' OR 1=1--。这样会有效地使SQL查询变成:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
这段代码选择所有用户名为空('')或者1=1(布尔类型,等同于真)的客户,然后使用--注释掉查询的其余部分。所以如果登录,它将使用第一个用户的权限进行登录,通常情况下是管理员权限。这将打印出整个客户表,或者对其进行任何操作。
而参数化查询则使用以下代码:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
其中,username和password是指向相关输入的变量。
现在,在这一点上,你可能会想,这根本没有改变什么。当然,你仍然可以在用户名字段中输入类似于Nobody OR 1=1'--的内容,从而有效地进行查询:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
这个看起来像是一个合理的论点。但是,你错了。
参数化查询的工作方式是将 sqlQuery 作为查询发送,数据库已经准确知道这个查询会做什么,然后只会将用户名和密码作为值插入。这意味着它们无法影响查询,因为数据库已经知道查询会做什么。所以在这种情况下,它将查找用户名为 "Nobody OR 1=1'--" 和一个空密码,应该返回 false。
不过这并不是一个完整的解决方案,仍需要进行输入验证,因为这不会影响其他问题,例如 XSS 攻击,因为您仍然可以将 JavaScript 放入数据库中。然后如果在页面中读取这些内容,根据任何输出验证,它将显示为正常的 JavaScript。因此,真正最好的方法仍然是使用输入验证,但使用参数化查询或存储过程来防止任何 SQL 攻击。
我在评论中发布的链接已经很好地解释了这个问题。以下是我对问题仍然存在的原因的总结:
那些刚开始学习的人可能没有意识到SQL注入。
有人知道SQL注入,但认为转义是(唯一的?)解决方案。如果你快速搜索 php mysql query
,第一页出现的是 mysql_query
页面,在该页面上有一个例子显示将经过转义的用户输入插入查询中。至少我看不到任何提到使用预处理语句的内容。正如其他人所说,有许多教程使用参数插值,所以它仍然被广泛使用并不令人惊讶。
缺乏对参数化语句工作原理的理解。有些人认为这只是一种高级的转义值的方法。
其他人知道预处理语句,但不使用它们,因为他们听说它们太慢了。我怀疑许多人听说过预处理语句的性能极差,但实际上并没有进行任何测试。正如Bill Karwin在他的演说中指出的那样,性能差异很少作为考虑使用预处理语句的因素。prepare once, execute many 的好处经常被忽视,以及安全性和代码可维护性的改进。
有些人在任何地方都使用参数化语句,但是插值未经检查的值,例如表和列名、关键字和条件运算符。动态搜索,例如允许用户指定多个不同的搜索字段、比较条件和排序顺序,就是这样的一个典型示例。
当使用ORM时,会有一种虚假的安全感。ORM仍然允许SQL语句部分的插值 - 参见5。
编程是一个庞大而复杂的主题,数据库管理也是如此,安全性更是如此。开发一个安全的数据库应用程序并不容易——即使是经验丰富的开发人员也可能会遇到问题。
在stackoverflow上很多回答并没有帮助。当人们编写使用动态SQL和参数插值的问题时,往往缺乏建议改用参数化语句的响应。有几次我提出使用预处理语句的建议被反驳,通常是因为被认为存在无法接受的性能开销。我严重怀疑这些提问者中大多数人都不会因为几毫秒额外的预处理时间对他们的应用程序产生灾难性的影响。
很好的问题。 答案更接近随机而非确定性,我将尝试用一个小例子来解释我的观点。
网络上有很多参考建议我们在查询中使用参数或者使用带参数的存储过程以避免SQL注入(SQLi)。我将向您展示存储过程(例如)并不是防止SQL注入的魔法棒。责任仍然在程序员身上。
考虑以下SQL Server存储过程,将从表“Users”中获取用户行:
create procedure getUser
@name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
'from Users '+
'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)
通过将用户名和密码作为参数传递,您可以获得结果。假设密码是明文(仅为此示例的简洁起见),正常调用应如下:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
但是这里我们有一个存储过程中程序员使用的不良编程技术,因此攻击者可以执行以下操作:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
上述参数将作为参数传递给存储过程,最终执行的SQL命令是:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
..将从用户中获取所有行
这里的问题在于,即使我们按照“创建存储过程并将要搜索的字段作为参数传递”的原则进行操作,SQLi仍然会被执行。这是因为我们只是在存储过程中复制了我们不良的编程实践。解决问题的方法是按以下方式重写我们的存储过程:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
我想说的是,开发者们必须首先了解什么是SQLi攻击以及如何进行攻击,然后相应地保护他们的代码。盲目遵循“最佳实践”并不总是更安全的方式......也许这就是为什么我们有那么多“最佳实践”失败的原因!
PDO
默认情况下会模拟它们,因此存在边缘情况的攻击。
如果您使用真正的预处理语句,一切都是安全的。当然,只要您不将不安全的SQL串联到查询中作为无法准备表名的反应。
是的,教育是主要问题,还有遗留代码库。许多教程使用转义字符,但不幸的是,它们不能很容易地从Web中删除。是的,为什么还有这么多成功的SQL注入攻击?只是因为一些开发者太愚蠢而不能使用参数化语句吗?
SQL注入是代码注入的一个子集,其中数据和代码通过同一通道提供,数据被误认为是代码。参数化查询通过使用关于数据和代码的上下文来形成查询,从而防止发生这种情况。
在某些特定情况下,这还不足够。在许多DBMS中,可以使用存储过程动态执行SQL,从而在DBMS级别引入SQL注入漏洞。使用参数化查询调用这样的存储过程将无法防止利用该过程中的SQL注入。另一个例子可以在此博客文章中看到。
更常见的是,开发人员错误地使用功能。当正确完成时,代码通常如下所示:
db.parameterize_query("select foo from bar where baz = '?'", user_input)
有些开发人员会将字符串连接在一起,然后使用参数化查询,但这并没有实际上提供我们所寻求的数据/代码区分所提供的安全保证:
db.parameterize_query("select foo from bar where baz = '" + user_input + "'")
正确使用参数化查询可以有效防止SQL注入攻击,但并非绝对安全。
因为大部分代码并非从安全性的角度编写,而且管理层往往选择增加功能(尤其是那些可视化、有销售价值的功能)而不是保障安全/稳定性/可靠性(这是更难以推销的)。只有在安全问题成为问题时,安全才会成为一个问题。