【问题标题】:What is a good way to sanitize mysql in an old classic ASP site?在旧的经典 ASP 站点中清理 mysql 的好方法是什么?
【发布时间】:2015-07-07 13:49:08
【问题描述】:

我是一家电子制造商的一个非常古老、非常大且写得非常糟糕的经典 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(从访问日志中获取的任意尝试)给出了来自站点的以下响应:

用于 ODBC 驱动程序的 Microsoft OLE DB 提供程序错误“80004005” [MySQL][ODBC 5.3(w) 驱动程序][mysqld-5.1.42-community] 使用的 SELECT 语句具有不同的列数 /product.asp,第 14 行

这意味着注入成功了,但在这种情况下没有成功获取任何数据。但是,其他人会这样做。

该站点由数百个带有意大利面条代码的 ASP 文件组成,代码总计数千行,没有太多结构。因此,它不是参数化查询的选择。工作量很大,而且容易出错。

一件好事是代码中的所有输入参数始终通过txtval 函数传递,所以这里有机会通过扩充函数来做得更好。此外,由于所有 SQL 调用都是使用 conn.execute(SQL) 完成的,因此搜索和替换例如非常简单。 conn.execute(sanitize(SQL)) 所以这里也有机会做点什么。

鉴于这种情况,我有哪些选择可以防止或至少将 SQL 注入的风险降到最低?

非常感谢任何意见。

更新:

1。 我明白参数化查询是处理问题的正确方法。我在创建网站时自己使用它。但考虑到网站的搭建方式和规模,修改、测试和调试需要1-2个月的时间。即使这就是我们最终的结果(我对此表示怀疑),我现在也需要做点什么。

2。 用 html 实体替换不是错字。它用它的 html 实体替换单引号。 (代码不是我写的!)

3。 在上面的具体示例中,使用 CInt(id) 可以解决问题,但它可以是任何东西,而不仅仅是数字输入。

更新 2:

好的,我知道我不是在寻求正确的解决方案。我从一开始就知道这一点。这就是我写“鉴于情况”的原因。

但是,过滤 mysql 关键字(如 selectunion 等)的输入至少会使它变得更好。不好,但好一点。这就是我所要求的,让它变得更好一点的想法。

虽然我很欣赏您的 cmets,但告诉我唯一好的选择是使用参数化查询并没有真正的帮助。 因为我已经知道了:)

【问题讨论】:

  • 我知道您说过它们不是一个选项,但请理解这是保护此类查询免受 SQL 注入攻击的基础之一:参数化查询,例如“从 id 的产品中选择 * =@Id",然后在执行之前在代码中构建/添加 ID 参数。我想,它不会让你变得比现在更糟糕。随处手动添加清理代码不会减少劳动强度或不易出错。
  • 咬紧牙关,正确修复网站。从长远来看,创可贴解决方案永远不会奏效......
  • 没有“快速修复”,就像@DavidW 说的那样,您尝试的任何解决方法都可能需要很长时间才能实施,而且不会那么健壮。
  • 你的困境应该被用作广告,说明为什么使用参数化查询应该是一个起点,为什么在开发过程中需要关注安全性,而不是需要关注的问题稍后解决,最后附上。很多次我都看到关于“我现在不关心 sql 注入”或“我是唯一的用户”或“这只是一个学习项目”的回应。快进到你所处的困境:“参数化查询不是一个选择——工作量很大”)。
  • 你不会找到任何“临时快速修复”来处理大量的错误/不安全代码。这就是重点。也许真正的解决方案是加速重写并将目前用于不存在的“快速修复”的资源投入到应用程序的“新”版本中正确编写的数据库代码中。

标签: mysql security vbscript asp-classic sql-injection


【解决方案1】:

我不会放弃参数化查询。它们是您可以用来保护自己免受 SQL 注入攻击的最佳工具。如果您的计划是替换所有这些调用:

conn.execute(SQL)

这些电话:

conn.execute(sanitize(SQL))

那么您已经在考虑修改与 SQL 的每个交互(顺便说一句,不要忘记 Command.Execute()Recordset.Open(),它们也可用于运行 SQL 语句)。由于您已经计划更改这些调用,请考虑调用自定义函数来运行语句。例如,替换:

set rs = conn.execute(SQL)

与:

set rs = MyExecute(SQL)

然后使用您的自定义函数使用Command 对象来设置适当的参数化查询。您需要巧妙地解析此自定义函数中的 SQL 语句。识别where 子句中的值,确定它们的类型(也许您可以查询表模式),并相应地添加参数。但可以做到。

您也可以借此机会清理输入内容。例如,使用 RegExp 对象从数字字段中快速去除 [^0-9\.]

但仍有机会从此函数返回一个记录集,该记录集将用于将值直接写入页面,而无需先进行 HTML 编码。这是一个真正的问题,特别是因为听起来您的网站过去已经成为目标。我不会相信来自您的数据库的任何数据。我在这里看到的唯一选项(不会涉及触摸每一页)是返回一个“干净”的 HTML 编码记录集,而不是默认记录集。

不幸的是,你还没有走出困境。 XSS 攻击可以通过 QueryString 参数、cookie 和表单控件来完成。知道 XSS 仍然存在非常现实的可能性后,在“修复”SQL 注入问题之后,您会有多安全的感觉?

我的建议?向您的主管解释困扰您网站的安全威胁,并说服他/她需要进行彻底审查或完全重写。投入“旧的、已经运行的网站”似乎需要大量资源,但一旦有人破坏您的网站或截断您的数据库表,您会希望自己投入时间。

【讨论】:

  • 我已经尝试过类似于 set rs = MyExecute(SQL) 的方法,但返回的记录集无效。我认为 ASP 不支持函数返回值中的对象。
  • @marlar 它确实支持返回对象只要记住在返回它时使用Set Set MyExecute = rs (其中rsADODB.RecordsetMyExecute() 函数内实例化).
  • @Lankymart:感谢您的建议!
【解决方案2】:

这种攻击应该只影响在您的 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 变量的通用代码(即在包含文件中)?

如果是这样,那么您可以替换该代码并使用OpenCloseExecute 函数(至少)创建自己的类。如果在您的代码中使用它们,您可能还需要其他方法。

这样您就可以有效地覆盖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

希望这里有一些帮助。

【讨论】:

  • txtval() 用于各种输入,不幸的是。所有文件都包含一个小型通用文件,负责处理数据连接。所以确实可以在这里做点什么。但是,我已经尝试过创建自己的执行函数,但显然 ASP 不允许函数返回对象,因此记录集(在我的示例中为 rs)无效。我该如何处理这个问题?
  • 嗯……至少在我的脑海里听起来是个好主意。只要使用Set,就不会意识到返回对象的任何问题。还有另一个 SO 问题可能会有所帮助...stackoverflow.com/questions/3837902/…
  • 想一想可能还有哪些其他解决方案不需要您通过大量经典 ASP 文件来修复...编写自己的 ISAPI 过滤器怎么样?
  • 感谢有关使用 Set 的更新。您能详细说明一下 ISAPI 过滤器吗?我知道 ISAPI 过滤器是什么,但想评论一下它如何用于此目的。
  • 创建一个 ISAPI 过滤器,为所有 .asp 文件运行并查找常见的 SQL 注入方法,如果发现重定向、过滤或结束请求。网上似乎有不少关于创建和使用过滤器的教程。
猜你喜欢
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多