尝试访问

http://mdsec.net/addressbook/67/

9.2.9 二阶SQL注入

一种特别有益的避开过滤的方法与二阶SQL注入(second-order SQL injection)有关。当数据首次插入数据库中时,许多应用程序能够安全处理这些数据。但是,一旦数据存储在数据库中,随后应用程序本身或其他后端进程可能会以危险的方式处理这些数据。许多这类应用程序并不像面向因特网的主要应用程序一样安全,但却拥有较高权限的数据库账户。

在一些应用程序中,用户输入在到达时通过转义单引号来进行确认。在前面搜索书籍的示例中,这种方法明显有效。当用户输入搜索项O'Reilly时,应用程序执行以下查询:

img251a

在这个查询中,用户提交的单引号被转换为两个单引号,因而传送给数据库的搜索项与用户最初输入的表达式具有相同的字符含义。

与单引号配对方法有关的问题出现在更复杂的情形中,此时同一个数据项被提交给几个SQL查询,然后写入数据库被几次读取。这是证明简单输入确认相对于边界确认存在不足的一个示例,如第2章所述。

回到前面那个允许用户自我注册并且在一个INSERT语句中存在SQL注入漏洞的应用程序。假设开发者将修复出现在用户数据中的所有单引号配对导致的漏洞。注册用户名foo'来建立如下查询,它不会在数据库中造成问题:

img251b

目前为止一切正常。然而,假设应用程序还执行密码修改功能,那么只有通过验证的用户才能够访问这项功能,而且为了加强保护,应用程序要求用户提交原始密码。然后应用程序从数据库中提取用户的当前密码,并对两个字符串进行比较,核对用户提供的密码是否正确。要完成核对任务,它首先要从数据库提取用户的用户名,然后建立如下查询:

img251c

因为保存在数据库中的用户名是字面量字符串foo',当应用程序提出访问要求时,数据库即返回这个值;只有在字符串被传送给数据库时才使用配对的转义序列。因此,当应用程序重复使用这个字符串并将它嵌入到另一个查询中时,就会造成一个SQL注入漏洞,用户最初的恶意输入就被嵌入到查询中。当用户尝试修改密码时,应用程序返回以下消息,暴露了上述缺陷:

img251d

要利用这种漏洞,攻击者只需注册一个包含专门设计的输入用户名,然后尝试修改密码。例如,如果注册如下用户名:

img251e

注册步骤将会被应用程序安全处理。如果攻击者尝试修改密码,他注入的查询就会执行,导致生成以下消息,泄露管理员的密码:

img251f

攻击者已经成功避开旨在阻止SQL注入攻击的输入确认,现在他能够在数据库中执行任意查询并获得查询结果。