SQL注入
翻译
原文:https://portswigger.net/web-security/sql-injection
- name: 翻译
desc: 原文:https://portswigger.net/web-security/sql-injection
bgColor: '#F0DFB1'
textColor: 'green'
2
3
4
SQL注入是一个古老的黄金漏洞,导致了许多引人注目的数据泄露。
尽管学习起来相对简单,但它可以潜在地用于一些高度严重的漏洞。这使得它成为初学者理想的第一个主题,甚至对于更有经验的用户也是必要的知识。
访问官方链接 https://portswigger.net/web-security/sql-injection (opens new window) 以学习SQL注入
# 0SQL注入
在本节中,我们将解释什么是SQL注入(SQLi,SQL injection),描述一些常见的示例,解释如何发现和利用各种SQL注入漏洞,并总结如何防范SQL注入。
实验室
如果你已经熟悉SQLi漏洞背后的基本概念,并且只想在一些实际的、易受攻击的目标上练习和利用它们,那么你可以从下面的链接访问本主题中的所有实验室。
# 1什么是SQL注入?
SQL注入(SQLi)是一种Web安全漏洞,允许攻击者干扰应用程序 并对其数据库进行查询。
它通常允许攻击者查看他们无法检索的数据。这可能包括属于其他用户的数据,或者应用程序本身能够访问的任何其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而导致应用程序的内容或行为发生持久更改。
在某些情况下,攻击者可以升级SQL注入攻击 以破坏底层服务器或其他后端基础设施,或执行拒绝服务攻击。
提示
官方在此处提供了一个YouTuBe的视频链接:https://www.youtube.com/embed/wX6tszfgYp4?origin=https://portswigger.net&rel=0 (opens new window)
# 2SQL注入攻击成功时的影响是什么?
成功的SQL注入攻击可能导致对敏感数据的未授权访问,例如密码、信用卡详细信息或用户个人信息。近年来,许多备受瞩目的数据泄露都是SQL注入攻击的结果,导致声誉受损和监管部门罚款。
在某些情况下,攻击者可以获得进入组织系统的持久后门,导致长期的威胁,可以在很长一段时间内不被注意到。
# 3SQL注入示例
有各种各样的SQL注入漏洞、攻击和技术,它们出现在不同的情况下。一些常见的SQL注入示例包括:
检索隐藏数据 (opens new window),你可以在其中修改SQL查询以返回其他结果。
颠覆应用程序逻辑 (opens new window),你可以更改查询来干扰应用程序逻辑。
UNION攻击 (opens new window),可以从不同的数据库表中检索数据。
检查数据库 (opens new window),从中可以提取关于数据库版本和结构的信息。
SQL盲注 (opens new window),你控制的查询结果不会在应用程序的响应中返回。
# 4检索隐藏数据
假设有一个购物应用程序,它以不同的类别来显示产品。
当用户点击gift类别时,他们的浏览器会请求URL:
https://insecure-website.com/products?category=Gifts
这将导致应用程序执行 SQL 查询,并从数据库中检索相关产品的详细信息:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
这个 SQL 查询要求数据库返回:
- 所有数据(*)
- 从 products 表中
- category 字段等于 Gifts
- 并且 released 字段等于1
限制条件released = 1
用于隐藏未发布的产品。对于未发布的产品,假设released = 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
始终为真,查询将返回所有项。
警告
在向 SQL 查询中注入OR 1=1
条件时要小心。
尽管这在注入的初始上下文中可能是无害的,但应用程序在多个不同的查询中 使用来自单个请求的数据是很常见的。
例如,如果条件达到UPDATE
或DELETE
语句,则可能导致数据意外丢失。
- name: 实验室-学徒
desc: WHERE子句中的SQL注入漏洞,允许检索隐藏数据 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data
bgColor: '#001350'
textColor: '#39d50c'
2
3
4
5
6
# 5颠覆应用程序逻辑
假设有一个登录类应用程序,它允许用户使用 用户名和密码 进行登录。
如果用户提交用户名wiener
和密码bluecheese
,应用程序通过执行以下 SQL 查询来检查凭据:
SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
如果该查询返回了用户的详细信息,则登录成功。否则将被拒绝。
在这里,攻击者可以使用 SQL 注释序列--
,从查询的WHERE
子句中删除密码检查,并作为任何用户登录,而无需密码。例如,提交用户名administrator'--
和一个空密码,结果如下:
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
该查询返回用户名为administrator
的用户,攻击者以该用户身份成功登录。
- name: 实验室-学徒
desc: 允许绕过登录的SQL注入漏洞 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/sql-injection/lab-login-bypass
bgColor: '#001350'
textColor: '#39d50c'
2
3
4
5
6
# 6从其他数据库表中检索数据
在应用程序响应中返回 SQL 查询结果的情况下,攻击者可以利用 SQL 注入漏洞,从数据库中的其他表中检索数据。
这是使用UNION
关键字完成的,它允许你执行额外的SELECT
查询并将结果附加到原始查询。
例如,如果应用程序执行以下查询,其中包含用户输入 “Gifts” :
SELECT name, description FROM products WHERE category = 'Gifts'
然后攻击者可以提交输入:
' UNION SELECT username, password FROM users--
这将导致应用程序返回所有用户名和密码 以及产品的名称和描述。
# 7检查数据库
在初步识别 SQL 注入漏洞后,获取有关数据库本身的一些信息通常很有用。这些信息通常可以为进一步利用铺平道路。
你可以查询数据库的版本详细信息。执行此操作的方式取决于数据库类型,因此你可以使用任何有效的技术推断数据库类型。例如,在 Oracle 上可以执行:
SELECT * FROM v$version
你还可以确定存在哪些数据库表,以及它们包含哪些列。例如,在大多数数据库中,可以执行以下查询来列出表:
SELECT * FROM information_schema.tables
# 8SQL盲注漏洞
许多 SQL 注入实例都是盲注漏洞。这意味着应用程序不会在其响应中返回 SQL 查询的结果或任何数据库错误的详细信息。
盲注漏洞仍然可以被利用,用来访问未经授权的数据,但所涉及的技术通常更复杂且难以执行。
根据漏洞的性质和所涉及的数据库,可以使用以下技术来利用 SQL 盲注漏洞:
- 你可以更改查询的逻辑,从而根据单个条件的真实性,在应用程序的响应中 触发可检测的差异。这可能包括 在一些布尔逻辑中注入一个新的条件,或者有条件地触发一个错误,比如被零整除。
(布尔盲注)
- 你可以在查询处理过程中 有条件地触发时间延迟,从而允许你根据应用程序响应所需的时间 推断条件的真实性。
(时间盲注)
- 你可以使用 OAST (opens new window) 技术触发带外网络交互。这种技术非常强大,可以在其他技术无法做到的情况下工作。通常,你可以通过带外通道直接窃取数据,例如,将数据放入你控制的域的 DNS 查找中。
(DNSLOG盲注)
# 9如何检测 SQL 注入漏洞
使用 Burp Suite 的Web漏洞扫描器 (opens new window)可以快速可靠地发现大多数SQL注入漏洞。
可以通过 对应用程序中的每个入口点 使用一组系统性的测试,从而手动检测 SQL 注入。这通常涉及:
- 提交单引号字符
'
并查找错误或其他异常。 - 提交一些特定于 SQL 的语法,该语法的计算结果为 入口点的基(原始)值和不同的值,并在应用程序响应中 查找差异。
- 提交布尔条件,如
OR 1=1
和OR 1=2
,并查找应用程序响应中的差异。 - 提交 用于在 SQL 查询中 触发时间延迟的有效负载,并查找响应时间的差异。
- 提交 OAST 有效负载,以便在 SQL 查询中 触发带外网络交互,并监视任何 因此而产生的交互。
# 10在不同的查询部分中注入SQL
大多数 SQL 注入漏洞都出现在SELECT
查询的WHERE
子句中。经验丰富的测试人员通常都很了解这种类型的 SQL 注入。
但是,SQL 注入漏洞在原则上,可以出现在 查询中的任何位置 和 不同的查询类型中。其他最经常出现 SQL 注入的地方是:
- 在
UPDATE
语句中,在更新的值或WHERE
子句中。 - 在
INSERT
语句中,在插入的值中。 - 在
SELECT
语句中,在表名 或 列名内。 - 在
SELECT
语句的ORDER BY
子句中。
# 11不同上下文中的 SQL 注入
到目前为止,在所有实验室中,你都使用查询字符串来注入恶意的 SQL 有效负载。
但是,请务必注意,你可以使用应用程序 作为 SQL 查询处理的任何可控输入,从而执行 SQL 注入攻击。例如,一些网站采用 JSON 或 XML 格式的输入,并使用它来查询数据库。
这些不同的格式,甚至可以为你提供替代方法,通过混淆攻击 (opens new window)来绕过由于 WAF 和其他防御机制 而被阻止的攻击。
“弱实现” 通常只是在请求中查找常见的 SQL 注入关键字,因此你可以通过对“被禁止的关键字”进行简单地编码或转义,从而绕过这些过滤器。
例如,以下基于 XML 的 SQL 注入,使用 XML 转义序列对SELECT
中的S
字符进行编码:
<stockCheck>
<productId>
123
</productId>
<storeId>
999 SELECT * FROM information_schema.tables
</storeId>
</stockCheck>
2
3
4
5
6
7
8
- name: 实验室-从业者
desc: 通过 XML 编码绕过过滤器的 SQL 注入 >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/sql-injection/lab-sql-injection-with-filter-bypass-via-xml-encoding
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
# 12二阶SQL注入
当应用程序从 HTTP 请求中获取用户输入,并在处理该请求的过程中,以不安全的方式将输入合并到 SQL 查询中时,就会出现一阶 SQL 注入。
在二阶 SQL 注入(也称为存储 SQL 注入)中,应用程序从 HTTP 请求中获取用户输入,并将其存储起来以备将来使用。这通常是通过数据库来完成的,但是在存储数据的地方不会产生漏洞。稍后,当处理不同的 HTTP 请求时,应用程序检索存储的数据,并以不安全的方式将其合并到 SQL 查询中。
二阶 SQL 注入通常出现在开发人员意识到 SQL 注入漏洞的情况下,因此可以安全地处理输入到数据库中的初始位置。当数据稍后被处理时,它被认为是安全的,因为它之前被安全地放入数据库中。此时,数据以不安全的方式处理,因为开发人员错误地认为它是受信任的。
# 13数据库特定因素
SQL 语言的一些核心特性,在流行的数据库平台上以相同的方式实现,因此许多检测和利用 SQL 注入漏洞的方法,在不同类型的数据库上都是相同的工作方式。
但是,常见数据库之间也存在许多差异。这意味着一些用于检测和利用 SQL 注入的技术,在不同平台上的工作方式不同。例如:
- 字符串 串联 的语法。
- 注释。
- 批处理(堆叠)查询。
- 特定于平台的 API 。
- 错误信息。
# 14如何防范SQL注入
通过在查询中使用参数化查询(预编译语句)而不是字符串连接,可以防范大多数 SQL 注入实例。
以下代码容易受到 SQL 注入的影响,因为用户输入直接连接到查询中:
String query = "SELECT * FROM products WHERE category = '" + input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
2
3
可以很轻松地重写此段代码,以防止用户输入干扰查询结构:
PreparedStatement statement = connection.PrepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();
2
3
参数化查询,可用于不受信任的输入 在查询中显示为数据的任何情况,包括WHERE
子句和INSERT
或UPDATE
语句中的值。但它不能用于处理查询 其他部分的不可信输入,例如表名或列名,或ORDER BY
子句。
将不受信任的数据放入查询部分 的应用程序功能 将需要采用不同的方法,例如 将允许的输入值列入白名单,或者使用不同的逻辑来交付所需的行为。
为了使参数化查询能够有效地防范 SQL 注入,查询中使用的字符串 必须始终是硬编码常量,并且不得包含来自任何源的任何变量数据。不要试图逐个判断数据项是否可信,并继续在查询中使用字符串拼接,来表示认为安全的情况。
很容易对数据的可能来源犯错误,或者在其他代码中的更改,违反了关于哪些数据被污染的假设。