1. 网络安全学会
  2. SQL 注入

SQL 注入

在本节中,我们将解释什么是SQL注入,描述一些常见示例,解释如何查找和利用各种SQL注入漏洞以及概述如何防止SQL注入。

什么是SQL注入(SQLi)?

SQL注入是一个网络安全漏洞,攻击者可以利用该漏洞来干扰应用程序对其数据库的查询。 通常,它使攻击者可以查看他们通常无法检索的数据。 这可能包括属于其他用户的数据,或者应用程序本身能够访问的任何其他数据。 在许多情况下,攻击者可以修改或删除此数据,从而导致应用程序内容或行为的永久更改。

在某些情况下,攻击者可以升级SQL注入攻击,以破坏基础服务器或其他后端基础结构,或者执行拒绝服务攻击。

SQL injection

成功的SQL注入攻击有什么影响?

成功的SQL注入攻击可能导致未经授权访问敏感数据,例如密码,信用卡详细信息或个人用户信息。 近年来,许多引人注目的数据泄露是SQL注入攻击的结果,导致声誉受损和监管罚款。 在某些情况下,攻击者可以获取组织系统的持久后门,从而导致长期的折衷,而这可能会在很长一段时间内被忽略。

SQL注入示例

在不同情况下会出现各种SQL注入漏洞、攻击和技术。 一些常见的SQL注入示例包括:

  • 检索隐藏的数据,您可以在其中修改SQL查询以返回其他结果。
  • 颠覆应用程序逻辑,您可以在其中更改查询以干扰应用程序的逻辑。
  • UNION攻击,您可以在其中从不同的数据库表中检索数据。
  • 检查数据库,您可以在其中提取有关数据库版本和结构的信息。
  • 盲目的SQL注入,您控制的查询结果不会在应用程序的响应中返回。

检索隐藏数据

考虑一个显示不同类别产品的购物应用程序。 当用户单击“Gifts”类别时,其浏览器将请求URL:

https://insecure-website.com/products?category=Gifts

这将导致应用程序执行SQL查询,以从数据库中检索相关产品的详细信息:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

此SQL查询要求数据库返回:

  • 所有细节 (*)
  • 从产品表
  • 类别是Gifts
  • 并发布为1。

限制 released= 1用于隐藏未发布的产品。 对于未发布的产品,假定发布= 0。

该应用程序未对SQL注入攻击实施任何防御措施,因此攻击者可以构建如下攻击:

https://insecure-website.com/products?category=Gifts'--

这将导致SQL查询:

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

这里的关键是双破折号序列-是SQL中的注释指示符,意味着查询的其余部分将被解释为注释。 这有效地删除了查询的其余部分,因此不再包含AND Release =1。这意味着将显示所有产品,包括未发布的产品。

更进一步,攻击者可以使应用程序显示任何类别的所有产品,包括他们不知道的类别:

https://insecure-website.com/products?category=Gifts'+OR+1=1--

这将导致SQL查询:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

修改后的查询将返回类别为Gifts或1等于1的所有项目。由于1 = 1始终为true,因此查询将返回所有项目。

颠覆应用逻辑

考虑一个允许用户使用用户名和密码登录的应用程序。 如果用户提交用户名wiener和密码bluecheese,则应用程序将通过执行以下SQL查询来检查凭据:

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

如果查询返回用户的详细信息,则登录成功。 否则,它将被拒绝。

在这里,攻击者只需使用SQL注释序列即可以没有密码的任何用户身份登录-从查询的WHERE子句中删除密码检查。 例如,提交用户名“ administrator”和空白密码将导致以下查询:

SELECT * FROM users WHERE username = 'administrator'--' AND password = ''

该查询返回用户名为administrator的用户,并以该用户身份成功登录攻击者。

从其他数据库表检索数据

如果在应用程序的响应中返回了SQL查询的结果,则攻击者可以利用SQL注入漏洞从数据库中的其他表中检索数据。 这是使用UNION关键字完成的,该关键字使您可以执行附加的SELECT查询并将结果附加到原始查询中。

例如,如果应用程序执行以下查询,其中包含用户输入“ Gifts”:

SELECT name, description FROM products WHERE category = 'Gifts'

然后攻击者可以提交输入:

' UNION SELECT username, password FROM users--

这将导致应用程序返回所有用户名和密码以及产品名称和描述。

检查数据库

在初步识别出SQL注入漏洞之后,获取有关数据库本身的一些信息通常非常有用。 这些信息通常可以为进一步开发铺平道路。

您可以查询数据库的版本详细信息。 完成此操作的方式取决于数据库类型,因此您可以从任何一种技术推断出数据库类型。 例如,在Oracle上,您可以执行:

SELECT * FROM v$version

您还可以确定存在哪些数据库表以及它们包含哪些列。 例如,在大多数数据库上,您可以执行以下查询以列出表:

SELECT * FROM information_schema.tables

盲目的SQL注入漏洞

SQL注入的许多实例都是盲目的漏洞。 这意味着该应用程序不会在其响应内返回SQL查询的结果或任何数据库错误的详细信息。 盲目漏洞仍然可以被利用来访问未经授权的数据,但是所涉及的技术通常更加复杂并且难以执行。

根据漏洞的性质和所涉及的数据库,可以使用以下技术来利用盲目的SQL注入漏洞:

  • 您可以更改查询的逻辑,以根据单个条件的真实性触发应用程序响应中可检测到的差异。 这可能涉及将新条件注入某些布尔逻辑,或有条件地触发诸如除零的错误。
  • 您可以有条件地触发查询处理的时间延迟,从而允许您根据应用程序响应的时间推断条件的真相。
  • 您可以使用OAST技术触发带外网络交互。 该技术非常强大,可以在其他技术无法使用的情况下使用。 通常,您可以直接通过带外通道泄露数据,例如,将数据放入您控制的域的DNS查找中。

如何检测SQL注入漏洞

使用Burp Suite的web漏洞扫描程序可以快速可靠地找到大多数SQL注入漏洞。

通过对应用程序中的每个入口点使用系统化的测试集,可以手动检测SQL注入。 这通常涉及:

  • 提交单引号字符'并查找错误或其他异常。
  • 提交一些特定于SQL的语法,以评估入口点的基础(原始)值和其他值,并在产生的应用程序响应中寻找系统差异。
  • 提交布尔条件(例如OR 1 = 1和OR 1 = 2),并查找应用程序响应中的差异。
  • 提交旨在在SQL查询中执行时触发时间延迟的有效负载,并寻找响应时间的差异。
  • 提交旨在在SQL查询中执行时触发带外网络交互的OAST有效负载,并监视所有结果交互。

在查询的不同部分注入SQL

大多数SQL注入漏洞出现在SELECT查询的WHERE子句中。 有经验的测试人员通常会很好地理解这种SQL注入。

但是,原则上,SQL注入漏洞可以出现在查询中的任何位置以及不同的查询类型中。 发生SQL注入的最常见其他位置是:

  • 在UPDATE语句中,在更新的值或WHERE子句中。
  • 在INSERT语句中,在插入的值内。
  • 在SELECT语句中,表或列的名称内。
  • 在SELECT语句中的ORDER BY子句中。

二阶SQL注入

一阶SQL注入出现在应用程序从HTTP请求获取用户输入的情况下,并且在处理该请求的过程中,以不安全的方式将输入合并到SQL查询中。

在二阶SQL注入(也称为存储的SQL注入)中,应用程序从HTTP请求中获取用户输入并将其存储以备将来使用。 这通常是通过将输入放入数据库来完成的,但是在存储数据时不会出现漏洞。 稍后,当处理其他HTTP请求时,应用程序将以不安全的方式检索存储的数据并将其合并到SQL查询中。

Second-order SQL injection

二阶SQL注入通常发生在开发人员意识到SQL注入漏洞并因此安全地处理输入到数据库中的初始位置的情况下。 以后处理数据时,由于先前已将其安全地放置到数据库中,因此认为该数据是安全的。 此时,由于开发人员错误地认为数据是可信的,因此以不安全的方式处理数据。

数据库特定因素

在流行的数据库平台上,SQL语言的某些核心功能以相同的方式实现,因此,检测和利用SQL注入漏洞的许多方式在不同类型的数据库上均相同。

但是,常见数据库之间也存在许多差异。 这意味着用于检测和利用SQL注入的某些技术在不同平台上的工作方式不同。 例如:

  • 字符串连接的语法。
  • 注释。
  • 批处理(或堆叠)查询。
  • 平台特定的API。
  • 错误消息。

如何防止SQL注入

通过使用参数化查询(也称为预处理语句)而不是查询中的字符串串联,可以防止大多数SQL注入实例。

由于用户输入直接连接到查询中,因此以下代码容易受到SQL注入的攻击:

String query = "SELECT * FROM products WHERE category = '"+ input + "'";

Statement statement = connection.createStatement();

ResultSet resultSet = statement.executeQuery(query);

可以以防止用户输入干扰查询结构的方式轻松重写此代码:

PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");

statement.setString(1, input);

ResultSet resultSet = statement.executeQuery();

参数化查询可用于不信任输入在查询中显示为数据的任何情况,包括WHERE子句和INSERT或UPDATE语句中的值。 它们不能用于处理查询其他部分中的不可信输入,例如表名或列名或ORDER BY子句。 将不受信任的数据放入查询的那些部分的应用程序功能将需要采用不同的方法,例如将允许的输入值列入白名单,或者使用不同的逻辑来传递所需的行为。

为了使参数化查询有效地防止SQL注入,查询中使用的字符串必须始终是硬编码常量,并且绝不能包含来自任何来源的任何变量数据。 不要试图逐案决定数据项是否受信任,并继续在查询中使用字符串连接来处理认为安全的情况。 可能会很容易就数据的原始来源犯错误,也可能因为其他代码的更改而违反关于污染数据的假设。

Try Burp Suite for Free

使用Burp Suite查找SQL注入漏洞

免费试用

Daily Swig关于SQL注入的案例