我是电子制造商的一个非常古老,非常大且写得非常糟糕的经典ASP网站的维护者(但幸运的是不是创建者)。
安全是一个笑话。这是在将输入放入MySQL口之前对输入进行消毒的唯一方法:
Function txtval(data)
txtval = replace(data,"'","'")
txtval = trim(txtval)
End Function
productid = txtval(Request.QueryString("id"))
SQL = "SELECT * FROM products WHERE id = " & productid
Set rs = conn.execute(SQL)
正因为如此,该网站不幸(但也许并不奇怪)是SQL注入攻击的受害者,其中一些是成功的。
上面采用的简单方法还不够。也没有使用Server.HTMLEncode
。逃避斜线也无济于事,因为攻击非常复杂:
product.asp?id=999999.9+UnIoN+AlL+SeLeCt+0x393133353134353632312e39,0x393133353134353632322e39,0x393133353134353632332e39,0x393133353134353632342e39,0x393133353134353632352e39,0x393133353134353632362e39
上面的url(从访问日志中获取的任意尝试)给出了网站的回复:
Microsoft OLE DB Provider for ODBC Drivers error '80004005' [MySQL][ODBC 5.3(w) Driver][mysqld-5.1.42-community] The used SELECT statements have a different number of columns /product.asp, line 14
这意味着注入完成但在这种情况下没有成功获取任何数据。然而,其他人则这样做。
该网站由数百个ASP文件组成,其中意大利面条代码总计达数千行而没有太多结构。因此,不能选择参数化查询。这项工作将是巨大的,也容易出错。
但有一件好事是代码中的所有输入参数都始终通过txtval
函数传递,因此通过增加函数可以更好地完成这一操作。此外,由于所有SQL调用都是使用conn.execute(SQL)
完成的,所以用例如搜索和替换它是非常简单的。 conn.execute(sanitize(SQL))
所以这里也有机会对此采取行动。
鉴于这种情况,我有哪些选择来阻止或至少最小化SQL注入的风险?
非常感谢任何输入。
更新
1。 我明白参数化查询是处理问题的正确方法。我在创建网站时自己使用它。但考虑到网站的构建方式和大小,它需要1-2个月的时间来修改,测试和调试它。即使这是我们最终的结果(我怀疑),我现在需要做点什么。
2。 用html实体替换不是拼写错误。它用其html实体替换单引号。 (我没有制作代码!)
3。 在上面的具体示例中,使用CInt(id)可以解决问题,但它可以是任何东西,而不仅仅是数字输入。
更新2:
好的,我知道我不是要求正确的解决方案。我从一开始就知道。这就是为什么我写“鉴于情况”。
但是,对select
,union
等mysql关键字的过滤输入至少会使其变得更好。不好,但好一点。这就是我要求的,让它变得更好的想法。
虽然我感谢您的评论,但告诉我唯一不错的选择是使用参数化查询并没有多大帮助。 因为我已经知道了:)
答案 0 :(得分:4)
我不会放弃参数化查询。它们是您可以用来保护自己免受SQL注入的最佳工具。如果您的计划是替换所有这些电话:
conn.execute(SQL)
对这些电话:
conn.execute(sanitize(SQL))
然后您已经在考虑修改与SQL的每次交互(BTW,不要忘记Command.Execute()
和Recordset.Open()
,这也可能用于运行SQL语句)。由于您已经计划更改这些调用,请考虑调用自定义函数来运行语句。例如,替换:
set rs = conn.execute(SQL)
使用:
set rs = MyExecute(SQL)
然后使用您的自定义函数来设置使用Command
对象的正确参数化查询。您需要巧妙地解析此自定义函数中的SQL语句。确定where
子句中的值,确定它们的类型(可能您可以查询表模式),并相应地添加参数。但它可以做到。
您也可以借此机会消毒输入。例如,使用RegExp
对象快速从数字字段中删除[^0-9\.]
。
但是,您仍然有机会从此函数返回记录集,该记录集将用于直接将值写入页面而不首先进行HTML编码。这是一个真正的问题,特别是因为它听起来像您的网站过去已成为目标。我不会信任来自您数据库的任何数据。我在这里看到的唯一选项(不会涉及触摸每一页)就是返回一个" clean",HTML编码的记录集而不是默认的记录集。
不幸的是,你还没有走出困境。 XSS攻击可以通过QueryString参数,cookie和表单控件完成。在"修复"之后你会感到多么安全? SQL注入问题知道XSS仍然是一个非常现实的可能性吗?
我的建议?向您的主管解释困扰您网站的安全威胁,并说服他/她需要彻底审查或完全重写。在一个旧的,已经在工作的网站上投入可能看起来很多资源"但是当有人污损你的网站或截断你的数据库表时,你会希望你投入时间。
答案 1 :(得分:0)
此攻击应该只影响SQL中传递的数值。
根据相同的txtval
函数是否用于数值和字符串值(以及其他类似日期),可能有也可能没有快速修复。
如果txtval
仅用于数值(可能不太可能),那么您可以通过在值周围添加单引号来保护,例如:
Function txtval(data)
txtval = replace(data,"'","'")
txtval = "'" & trim(txtval) & "'"
End Function
如果它用于所有值类型,那么您唯一的选择可能是搜索所有代码并且:或者
1)为所有数字SQL添加单引号,例如:
SQL = "SELECT * FROM products WHERE id = '" & productid & "'"
2)创建一个新功能,仅用于清理数字值,然后更改所有查询以使用它(不是快速修复),例如:
Function numval(data)
If IsNumeric(data) Then
numvalue = CDbl(data)
Else
numvalue = 0 'or NULL?
End If
End Function
然后更改您的查询,例如:
productid = numval(Request.QueryString("id"))
SQL = "SELECT * FROM products WHERE id = " & productid
是否有公共代码(即在包含文件中)用于打开数据库并创建示例代码中使用的conn
变量?
如果是这样,那么您只需替换该代码并使用Open
,Close
和Execute
函数(至少)创建自己的类。如果在代码中使用它们,您可能还需要其他方法。
通过这种方式,您可以有效地覆盖execute
等行中的Set rs = conn.execute(SQL)
。
例如:
Class MyDatabase
Private m_conn
Public Sub Open(connString)
Set m_conn = Server.CreateObject("ADODB.Connection")
m_conn.Open connString
End Sub
Public Sub Close()
m_conn.Close
Set m_conn = Nothing
End Sub
Public Function Execute(sql)
'Sanitize input here (sql), simple example just for this type of attack
If InStr(sql, "UnIoN AlL SeLeCt") <> 0 Then sql = ""
'return a RecordSet
Set Execute = m_conn.Execute(sql)
End Property
End Class
然后改变你的共同conn声明......(例如)
Set conn = Server.CreateObject("ADODB.Connection")
...到...
Set conn = New MyDatabase
如果你保留txtval
函数,我也会更新它以逃避斜杠和单引号,例如:
Function txtval(data)
txtval = Replace(Replace(strValue, "'", "''"), "\", "\\")
txtval = trim(txtval)
End Function
希望这里的某些内容可能会有所帮助。