ice*_*gle 76 sql exploit sql-injection code-injection
如果是,为什么还有那么多成功的SQL注入?仅仅因为一些开发人员太愚蠢而无法使用参数化语句?
Mik*_*ike 63
我在评论中发布的链接很好地解释了问题.我总结了我对问题持续存在的看法,如下:
那些刚刚开始的人可能没有意识到SQL注入.
有些人知道SQL注入,但认为转义是(唯一的?)解决方案.如果您进行快速Google搜索php mysql query,则显示的mysql_query第一个页面是页面,其中有一个示例,显示将转义的用户输入插入到查询中.没有提及(至少不是我能看到)使用预准备语句.正如其他人所说,那里有很多使用参数插值的教程,它仍然使用的频率并不令人惊讶.
缺乏对参数化语句如何工作的理解.有些人认为这只是逃避价值观的一种奇特手段.
其他人知道参数化语句,但不要使用它们,因为他们听说它们太慢了.我怀疑很多人都听说过多么缓慢的典型语句,但实际上并没有对自己做过任何测试.正如Bill Karwin在他的演讲中指出的那样,在考虑使用准备好的陈述时,很少会将性能上的差异用作一个因素.准备一次,执行许多的好处,往往似乎被遗忘,安全性和代码可维护性的改进也是如此.
有些在任何地方都使用参数化语句,但是插入了未经检查的值,例如表和列名,关键字和条件运算符.动态搜索(例如允许用户指定多个不同搜索字段,比较条件和排序顺序的搜索)就是这样的主要示例.
使用ORM时的安全性错误.ORM仍允许插入SQL语句部分 - 参见5.
编程是一个大而复杂的主题,数据库管理是一个大而复杂的主题,安全是一个大而复杂的主题.开发安全的数据库应用程序并不容易 - 即使是经验丰富的开发人员也会被抓住
stackoverflow上的许多答案都无济于事.当人们编写使用动态SQL和参数插值的问题时,通常缺少建议使用参数化语句的响应.有几次,我让人们反驳我的建议,使用准备好的陈述 - 通常是因为认为不可接受的性能开销.我非常怀疑那些提出大多数问题的人处于准备参数化语句所花费的额外几毫秒将对其应用产生灾难性影响的位置.
Jos*_*vic 50
当文章谈论停止SQL攻击的参数化查询时,它们并没有真正解释原因,它通常是"它确实如此,所以不要问为什么" - 可能是因为他们不了解自己.一个不好的教育者的确定迹象是不能承认他们不知道的东西.但我离题了.当我说我发现被混淆完全可以理解时很简单.想象一下动态SQL查询
sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password
Run Code Online (Sandbox Code Playgroud)
所以一个简单的SQL注入只是将Username放入'OR 1 = 1--这将有效地使sql查询:
sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password
Run Code Online (Sandbox Code Playgroud)
这表示选择所有客户,他们的用户名是空白('')或1 = 1,这是一个布尔值,等于true.然后它使用 - 注释掉查询的其余部分.因此,这将打印出所有客户表,或者随意使用它,如果登录,它将以第一个用户的权限登录,这通常是管理员.
现在,参数化查询的执行方式不同,代码如下:
sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'
parameters.add("User", username)
parameters.add("Pass", password)
Run Code Online (Sandbox Code Playgroud)
其中用户名和密码是指向相关输入的用户名和密码的变量
现在,在这一点上,你可能会想,这根本不会改变任何东西.当然你仍然可以在用户名字段中输入类似Nobody OR 1 = 1'的内容,从而有效地进行查询:
sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'
Run Code Online (Sandbox Code Playgroud)
这似乎是一个有效的论点.但是,你错了.
参数化查询的工作方式是,sqlQuery作为查询发送,数据库确切知道此查询将执行的操作,然后才会将用户名和密码仅作为值插入.这意味着它们不能影响查询,因为数据库已经知道查询将执行什么操作.所以在这种情况下,它会寻找一个用户名"Nobody OR 1 = 1' - "和一个空密码,这应该是假的.
这不是一个完整的解决方案,并且仍然需要完成输入验证,因为这不会影响其他问题,例如XSS攻击,因为您仍然可以将javascript放入数据库.然后,如果这被读出到页面上,它将显示为普通的javascript,具体取决于任何输出验证.所以最好的办法仍然是使用输入验证,但使用参数化查询或存储过程来阻止任何SQL攻击.
And*_*ris 10
好问题.答案更具随机性而非确定性,我将尝试用一个小例子来解释我的观点.
网上有很多引用建议我们在查询中使用参数或者使用带参数的存储过程以避免SQL注入(SQLi).我将向您展示存储过程(例如)不是针对SQLi的魔术棒.责任仍然留在程序员身上.
请考虑以下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)
Run Code Online (Sandbox Code Playgroud)
您可以通过将用户名和密码作为参数传递来获得结果.假设密码是自由文本(仅为了简化此示例),正常的调用将是:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [dbo].[getUser]
@name = 'admin'
,@pass = '!@Th1siSTheP@ssw0rd!!'
GO
Run Code Online (Sandbox Code Playgroud)
但是在这里,程序员在存储过程中使用了一种糟糕的编程技术,因此攻击者可以执行以下操作:
DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)
EXECUTE @RC = [TestDB].[dbo].[getUser]
@name = 'admin'
,@pass = 'any'' OR 1=1 --'
GO
Run Code Online (Sandbox Code Playgroud)
上述参数将作为参数传递给存储过程,最终将执行的SQL命令是:
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'
Run Code Online (Sandbox Code Playgroud)
..which将从用户返回所有行
这里的问题是,即使我们遵循"创建存储过程并将字段传递给搜索作为参数"的原则,仍然执行SQLi.这是因为我们只是在存储过程中复制我们糟糕的编程习惯.解决问题的方法是重写我们的存储过程,如下所示:
alter procedure getUser
@name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID
from Users
where usrUName = @name and usrPass = @pass
Run Code Online (Sandbox Code Playgroud)
我想说的是,开发人员必须首先了解SQLi攻击是什么以及如何执行,然后相应地保护他们的代码.盲目地遵循"最佳实践"并不总是更安全的方式......也许这就是为什么我们有这么多'最佳实践' - 失败!
是的,使用预准备语句会停止所有SQL注入,至少在理论上是这样.在实践中,参数化语句可能不是真正准备好的语句,例如PDO在PHP中默认情况下模拟它们,因此它可以对边缘案例攻击开放.
如果您使用的是真实准备好的陈述,一切都是安全的.好吧,至少只要你不将不安全的SQL连接到你的查询中作为对例如不能准备表名的反应.
如果是,为什么还有那么多成功的SQL注入?仅仅因为一些开发人员太愚蠢而无法使用参数化语句?
是的,教育是这里的主要观点,也是遗留代码库.许多教程使用转义,遗憾的是,这些教程无法轻易从网络中删除.
| 归档时间: |
|
| 查看次数: |
26610 次 |
| 最近记录: |