SQL-注入策略-全-
SQL 注入策略(全)
原文:
annas-archive.org/md5/8346460628ce82f0f83fee4bb3858144
译者:飞龙
序言
互联网无处不在,它对我们的社会和经济生活至关重要,事实如此。
我们的通信能力、我们每天饮用的水、为我们夜晚提供光亮并驱动改善我们生活的物品(如洗衣机)的能源、交通运输和金融世界,都完全依赖于互联互通的系统。这些系统在大多数情况下使用软件来管理存储在数据库中的数据,而这些软件通常不仅能通过内部网络访问,还能通过外部网络访问。这就带来了最为严重的安全问题。
网络上每 39 秒就会发生一次攻击,每天有 30,000 个新网站被黑客入侵,黑客每秒盗取 75 条数据库记录。网络攻击者有多种途径可以侵入 Web 应用程序,但 SQL 注入仍然是他们最常用的选择。Akamai 的《互联网状况报告》显示,SQL 注入现在占所有 Web 应用攻击的近三分之二(65.1%)。
我们希望通过本书,开发人员能够构建更安全的系统,安全测试人员能够在开发早期发现可能导致 SQL 注入的漏洞。
本书适合人群
本书为两类读者设计。它主要面向有基本编程经验的任何人(无论是移动端、Web 端还是后端领域),他们希望通过安全能力为自己的工作增值,以构建更具弹性的软件。安全从业者是本书的第二类目标读者。通过本书的内容,他们将能够更好地理解一些被黑客用来攻击全球系统的最关键漏洞。
本书采用循序渐进的方法;任何人都可以学习到有效的技术,以构建高安全性的软件或更好的应用安全测试,即使是在处理新的主题时,比如移动设备和物联网(IoT)。
本书内容
第一章,SQL 注入的结构化查询语言,作为该主题的理论介绍,简要描述了 SQL 是什么、它的用途以及它的可能弱点,这些弱点导致了 SQL 注入。这个理论概述对于理解 SQL 注入背后的概念至关重要,比如数据库管理系统、数据库模型和 SQL。
第二章,操控 SQL——利用 SQL 注入,继续以理论的方式探讨该主题,更多地接触到 SQL 注入攻击的实际操作。此章节包括许多不同目的的输入字符串示例,这些字符串可能触发 SQL 注入攻击。
第三章,环境设置,涵盖了本书核心实践部分将使用的测试环境的设置,并同时定义了其背后的主要方法。
第四章,攻击 Web、移动和物联网应用,主要涉及针对传统 Web 应用的 SQL 注入攻击,这是最常见的情境,使用手动和自动化技术,依赖我们在前一章中讨论的工具集。此外,我们还将看到,移动应用和物联网设备同样可能容易受到 SQL 注入攻击,并展示实际案例。
第五章,通过防御性解决方案防止 SQL 注入,重点介绍了防御方面:既然我们知道了这种令人印象深刻且具有破坏性的漏洞类型——以及它在原则上是如何简单地被利用的——我们该如何阻止它?
第六章,将一切整合起来,通过总结和分析我们所学到的内容,对本书的学习内容进行回顾,从批判的角度审视一切,并考虑不仅是 SQL 注入的广泛影响,还包括在一个依赖信息技术和数据的世界中,安全漏洞的更大影响。
为了从本书中获得最大收益
为了正确跟随书中的内容,你只需要一台 PC;操作系统的安装并不重要。进一步的要求将在书中逐步详细解释。然而,任何关于 Java、Android 开发和 SQL 语法的知识都将非常有用。
必要软件的安装将在书中根据需要讨论,涉及以下内容:
-
一个 Android 开发环境(Android Studio IDE,Android SDK—API Level 30 和 Android 虚拟设备)
-
Apache Tomcat 9.0
-
MySQL 8.0(开发套件)
-
Java 开发工具包(14.0.1)
-
Eclipse(企业 Java 开发者 IDE)
如果你使用的是本书的数字版本,我们建议你自己输入代码,或者通过 GitHub 仓库访问代码(链接将在下一部分提供)。这样做可以帮助你避免与代码复制/粘贴相关的潜在错误。
下载示例代码文件
你可以从 www.packt.com 上的账户下载本书的示例代码文件。如果你是从其他地方购买的此书,你可以访问 www.packtpub.com/support 注册,直接通过电子邮件获取文件。
你可以按照以下步骤下载代码文件:
-
登录或注册 www.packt.com。
-
选择支持标签。
-
点击代码下载。
-
在搜索框中输入书名,并按照屏幕上的指示操作。
下载文件后,请确保使用以下最新版软件解压或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
本书的代码包也托管在 GitHub 上,地址为 github.com/PacktPublishing/SQL-Injection-Strategies
。如果代码有更新,GitHub 仓库中的代码将会随之更新。
我们的丰富书籍和视频目录中还有其他代码包可供下载,网址是 https://github.com/PacktPublishing/。欢迎查看!
代码实践
本书的《代码实践》视频可以在 bit.ly/3fioIHt
观看。
下载彩色图像
我们还提供了一个 PDF 文件,里面包含了本书中使用的带有彩色图像的截图/图表。您可以在此下载:www.packtpub.com/sites/default/files/downloads/9781839215643_ColorImages.pdf
。
本书使用的约定
本书中使用了若干文本约定。
文本中的代码:表示文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入以及 Twitter 账号。例如:“让我们在用户名字段中尝试著名的 ' or 1=1 -- -
字符串……”
代码块的格式如下:
<urn:getUser soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
</urn:getUser>
</soapenv:Body>
</soapenv:Envelope>
粗体:表示新术语、重要单词或屏幕上显示的文字。例如,菜单或对话框中的文字以这种方式显示。举个例子:“点击 创建新虚拟机 按钮,并在向导中完成设置。”
提示或重要注意事项
以这种方式显示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果您对本书的任何部分有疑问,请在邮件主题中注明书名,并通过电子邮件发送至 customercare@packtpub.com。
勘误表:虽然我们已尽力确保内容的准确性,但难免会有错误。如果您在本书中发现错误,欢迎向我们报告。请访问 www.packtpub.com/support/errata,选择您的书籍,点击“勘误表提交表单”链接,并填写详细信息。
盗版:如果您在互联网上遇到任何我们作品的非法复制,欢迎提供相关位置或网站名称。请通过电子邮件将链接发送至 copyright@packt.com,我们会非常感激您的帮助。
如果您有兴趣成为作者:如果您在某个领域具有专长,并且有兴趣撰写或参与编写书籍,请访问 authors.packtpub.com。
评价
请留下评论。阅读并使用本书后,为什么不在您购买书籍的网站上留下评价呢?潜在读者可以看到并参考您的公正意见做出购买决策,我们在 Packt 能够了解您对我们产品的看法,而我们的作者也能看到您对他们书籍的反馈。谢谢!
欲了解更多关于 Packt 的信息,请访问 packt.com。
第一部分:理论上的(SQL)注入
本节作为 SQL 和 NoSQL 注入的理论基础,旨在更好地理解注入攻击和对策的实际方面。
本节包括以下章节:
-
第一章, SQL 注入的结构化查询语言
-
第二章, 操纵 SQL - 利用 SQL 注入
第一章:用于 SQL 注入的结构化查询语言
当今世界每天都依赖于网络空间的概念:互联网使全球各地的人们能够连接到世界任何地方的计算机。这使得通过依赖各种技术、协议和机制提供的服务能够即时使用,这些技术、协议和机制构成了万维网上一切可用资源的基础。不幸的是,安全问题对于这个复杂的连接和服务网络来说,和现实世界一样重要。
恶意代理每天在全球范围内对计算机发动攻击,主要是为了个人利益或优势。通过利用在线应用和服务,实际上有可能获得计算机或整个网络的控制,从而利用某些技术、协议、框架或应用的内在漏洞。最常见且臭名昭著的方式之一就是通过 SQL 注入,它是一种特定类型的攻击,试图利用数据库语言的语法——SQL
,即结构化查询语言——以访问通常无法获取的数据库信息,包括负责账户认证的数据库,其中包含用于访问服务的用户名和密码。尽管 SQL 注入是一种众所周知的攻击方式,但易受攻击的应用程序仍然存在,暗示着在应用开发的安全性方面,有时并没有得到足够的重视。
本书旨在通过从理论和实践两个方面解释 SQL 注入,深入探讨这个问题。
本章作为对这一问题的理论性介绍,高层次地描述了 SQL 是什么,它的用途,以及导致 SQL 注入的可能弱点。这一理论概述对于理解后续章节将进一步探讨的 SQL 注入背后的概念至关重要。
在介绍了数据库、数据库管理系统和模型、查询以及 SQL 特定概念后,将涉及语法和逻辑的各个方面,快速展示可能导致 SQL 使用中安全漏洞的主要构造和要素,最终引出核心问题:SQL 注入。
本章将涵盖以下主题:
-
SQL 概述——一种关系查询语言:对 SQL 的初步概述,SQL 注入通常基于这种语言,我们的主要参考语言,并与其他 DBMS 模型的关系模型进行比较。
-
SQL 的语法和逻辑:解释 SQL 背后的主要概念和构造,其中一些可能被恶意攻击者利用。
-
SQL 的安全隐患:对 SQL 中安全概念的简要介绍及其在应用中的使用。
-
SQL 使用中的弱点:解释依赖 SQL 的应用程序可能遇到的主要陷阱,并强调在安全开发中的一些通用建议。
技术要求
在本章及接下来的章节中,我们讨论的主题大多是理论性的。然而,我们建议你阅读 SQL 技术文档。为了参考,我们提供了 MySQL、Oracle 和 Microsoft SQL Server 的文档链接:
-
docs.oracle.com/en/database/oracle/oracle-database/index.html
-
docs.microsoft.com/en-us/sql/sql-server/?view=sql-server-ver15
SQL 概述 – 一种关系型查询语言
在计算机系统中,最常见的数据存储方式之一是依赖于数据库。数据库可以被视为大型软件容器,能够以结构化和可访问的方式存储大量信息,从而优化数据的存储和访问操作。
根据使用的方式和模型,达成这一目标的实现方式可能会有所不同。最常见的方法之一是使用关系模型,该模型基于关系代数,数据作为一系列记录被收集,描述了对象之间存在的关系。SQL 是一种基于这些概念的查询语言,并且在许多数据库系统中得到广泛应用。本节将深入讨论这些主题,首先解释数据库管理系统、关系型数据库和 SQL。
数据库管理系统和关系型数据库
如前所述,数据库的实现依赖于底层系统,或称数据库管理系统(DBMS)。DBMS 本质上是一款负责存储、访问、操作以及一般性地管理数据的软件,通过对收集和管理信息的特定定义来实现这些功能。
为了更好地理解它们之间的差异,本书现在将数据库系统分为两个大类。我们可以根据关系型模型在数据管理中的相关性,区分关系型数据库和非关系型数据库。
关系型数据库
关系型数据库由于其诸多优点,已被广泛认为是标准。数据以表格的形式收集,其中行表示对象,以记录的形式存储,列表示其属性。其名称来源于数据可以通过表格之间的公共属性进行关联和连接的方式,即通过关系。因此,关系代数的概念变得相关,它描述了通过结构化过程语言,如何管理数据表。SQL 是该模型最流行的代表,它利用了关系代数的大部分概念,从而提供了一个任何人都可以轻松使用的模型,即使没有编程经验,同时保持其整体效率:
](image/B15632_01_002.jpg)
图 1.1 – 由三张表组成的简单关系型模式,描述了汽车和车主的关系,并将唯一的 ID 放入关系中
非关系型数据库
非关系型(No-rel)数据库是被视为关系型模型替代方案的一类数据库管理系统(DBMS)模型,通常在数据库系统中占据更重要的位置。最初,NoSQL 这个术语用于定义这一类系统,但由于其误导性,后来被放弃:一些最早尝试构建非关系型数据库的努力实际上使用了关系模型的一些概念。非关系型数据库包含许多模型,其中一些如下:
- 网络数据库将数据建模为网络中的连接节点:
](image/B15632_01_004.jpg)
图 1.2 – 一个简单的网络模式,用于表示车主和汽车之间的所有权关系
- 基于图的数据库通过图形结构突出显示数据之间的连接,提供可导航的结构:
](image/B15632_01_003.jpg)
图 1.3 – 与关系型示例中相同的所有权关系,这次在基于图的模型模式中表示
- 面向对象数据库将数据建模为对象,类似于 Java 等编程语言中的方式:
](image/B15632_01_003.jpg)
图 1.4 – 在面向对象模型模式中表示的所有权关系
- 基于文档的数据库描述包含键值对的文档中的数据,指定数据的存储和管理方式,并提供一种不依赖于定义模式的灵活方法。基于文档的模型通常可以将嵌入的对象作为集合包含在单一键中,如下图所示:
图 1.5 – 一种基于文档的模型架构,可以表示所有权关系
尽管名字中有“SQL 注入”一词,但实际上,SQL 注入以某种形式可能影响所有现有的数据库模型。我们现在将重点讨论关系型数据库和 SQL。
SQL – 结构化查询语言
SQL 代表结构化查询语言,它是访问、导航和管理关系型数据库的主要工具。SQL 提供了一种结构化良好的语言,易于理解,得益于其类自然语言的命令和操作的清晰性,这些操作与特定的语言字符串相对应,接下来的章节将对此进行描述。
SQL 有许多不同的实现,取决于其所在的系统,这些实现之间存在一些细微的差异,部分差异将在下一章中更详细地解释,因为它们与 SQL 注入攻击直接相关。让我们来看看最流行的 SQL 实现。
MySQL
MySQL 是 SQL 的开源版本,广泛应用于许多 Web 应用框架和知名网站。它被认为是 SQL 技术的主要代表之一,也是一个整体表现良好的实现。
MySQL 被认为是开源 SQL 引擎中最好的实现之一,通常作为 SQL 语法的参考。
在这里,我们将列出一些关于 MySQL 的特别之处。
在字符序列方面,插入注释的方式不止一种:
-
#
-
/*comment*/
-
--
(这需要一个空格,后面跟任何字符,才能被解释为注释。在实际测试中,我们使用组合-- -
。) -
;%00
(%00
是空字符,在这里以 URL 编码的形式显示。这是一种非官方的注释插入方法,因为它没有出现在官方文档中。) -
通常,MySQL 系统中有两个默认数据库,它们始终存在于架构中:
-
mysql
(仅对特权用户可用) -
information_schema
(仅从 MySQL 版本 5 开始提供)
MySQL 支持一些函数和变量,如 VERSION()
和 @@VERSION
,用于获取 MySQL 服务器的版本信息。
SQLite
SQLite 提供了另一种方法,它将实现直接嵌入应用程序代码中,而不使用客户端-服务器架构。虽然它推荐用于轻量级应用,如移动应用,但由于一些内在的简化,它可能存在一些缺点。
SQLite 的主要特点是,它将信息存储在 SQLite 数据库文件中,而不需要客户端-服务器架构。因此,由于它是独立运行的,最好不要将其用于敏感操作,如身份验证,或者一般来说,不应用于存储敏感信息:任何访问文件系统的人都可以轻松查看整个数据库。
Oracle 数据库
Oracle 数据库,通常简称为 Oracle,是主要的专有 SQL 系统之一。尽管它最初是作为一个 SQL 关系型 DBMS 诞生的,但随着时间的推移,它开始支持不同的模型。因此,它被认为是一个多模型 DBMS。
在专有数据库系统方面,Oracle 是企业中最受欢迎的模型,因其与许多技术、编程语言和数据库模型具有广泛的兼容性。
和 MySQL 一样,Oracle 数据库在数据库结构和语法方面也有一些需要记住的特点。
与其他数据库系统不同,Oracle 数据库只支持通过字符序列插入注释的一种方式:--
。
Oracle 数据库系统也有两个默认数据库:
-
SYSTEM
-
SYSAUX
Microsoft SQL Server
Microsoft SQL Server 是企业世界中最常见的解决方案之一。它是一个为在 Windows Server 操作系统上运行而优化的 SQL DBMS,而 Windows Server 操作系统是最广泛采用的服务器操作系统之一。
Microsoft SQL Server
(MSSQL)也有自己的一些特点。
MSSQL 支持通过三种方式插入注释,形式为字符序列:
-
/*comment*/
-
--
-
%00
MSSQL 系统有许多默认的数据库,这些数据库在架构中始终存在:
-
pubs
-
model
-
msdb
-
tempdb
-
northwind
-
information_schema
(从 MSSQL Server 2000 起)
MSSQL 允许使用 @@VERSION
命令来检索数据库系统版本。
SQL 通常是一种高性能的语言,能够查询结构化数据。查询遵循特定的可读语法,允许用户和数据库管理员执行各种操作,从创建和删除表到根据特定条件提取数据。以下部分重点展示了基本的 SQL 语法和功能,暂时搁置语言实现差异,同时也将探讨所提到命令背后的逻辑。
SQL 的语法和逻辑
如前所述,SQL 是一种易于使用和理解的语言,能够执行多种不同类型的操作。像所有语言一样,它基于解释命令字符串,这些命令字符串插入时需遵循预期的语法,并且每个特定语句只对应一种操作。SQL 的主要语句可以有许多类型。让我们来看一下最重要的几个:
-
SELECT 语句:
SELECT
是最常用的 SQL 命令。其目的是允许数据库进行查询,显示满足(可选)特定条件的记录中的指定属性;例如:SELECT color, shape FROM objects
该语句显示了
objects
表中所有记录的color
和shape
属性。SQL 还允许使用通配符——在本例中是字符*
——来进行一般性的选择:SELECT * FROM objects
-
该语句将返回
objects
表中的所有记录,并显示所有属性。通过添加WHERE
子句指定条件,搜索还可以进一步细化:SELECT color, shape FROM objects WHERE color='blue'
该语句只会显示
color
字段值为blue
的记录:
图 1.6 – SQL 中的 SELECT 查询,并显示其结果
SELECT
语句中可以使用其他子句:
- DISTINCT 子句:用于返回没有重复的结果(SELECT DISTINCT color FROM objects)
- ORDER BY 子句:用于根据属性对结果进行排序(SELECT * FROM objects ORDER BY color ASC
为升序,DESC
为降序)
清楚理解SELECT
语句的工作原理对于掌握 SQL 注入非常重要。作为最常见的语句,滥用其结构是 SQL 注入攻击的主要推动力,它允许在看似无害的指令中插入其他 SQL 命令。我们将在下一章进一步详细介绍这一点。
-
INSERT 语句:
INSERT
语句用于以非常简单的方式将数据添加到数据库中,也就是指定要插入的属性值。我们来看一个例子:INSERT INTO objects (shape, color) VALUES (square, blue)
前面的语句通过分别为形状和颜色属性赋值方形和蓝色,将新数据添加到数据库中:
图 1.7 – SQL 中的 INSERT 查询,并显示其结果
另外,你可以在不指定属性的情况下添加数据(INSERT INTO objects VALUES (square, blue)),但请记住,由于未指定的属性会被设置为null
,这种方式仅在插入的完整记录顺序正确时才推荐使用。INSERT
语句也可能被恶意使用,例如,插入攻击者可用于获取系统访问权限的账户凭证。
-
CREATE 和 DROP 语句:正如它们的名字所示,
CREATE
和DROP
语句用于创建或删除整个 SQL 表,甚至是数据库。DROP
语句的语法非常简单,因为它仅仅删除指定表或数据库中的所有记录(DROP TABLE objects
,DROP DATABASE db),而CREATE
语句则可以根据目标以多种方式使用。创建数据库与DROP
语句类似,即只需要使用CREATE DATABASE db
,而表则显然需要特定的信息来创建属性。以objects
为例,我们可以使用以下代码:创建表
objects
(objID 整型,shape 字符串类型,color 字符串类型)。该语句将创建一个名为
objects
的表,其中objID
为整型,shape
和color
为字符串类型,最大长度设置为 32 个字符。 -
ALTER 语句:
ALTER
语句用于修改关于数据库或表的一般信息:ALTER DATABASE dbname1 MODIFY NAME=dbname2
上述语句将重命名数据库
dbname1
为dbname2
。以下语句也将向现有的表(objects)添加一个名为newcolumn
的新字符串字段,该字段最大存储 32 个字符:ALTER TABLE objects ADD newcolumn varchar(32)
ALTER TABLE
也可以用来删除整个属性(ALTER TABLE objects DROP newcolumn)或修改列的数据类型。由于DROP
语句通常用于禁用应用程序功能,ALTER
语句在 SQL 注入中通常不被使用。
这里列出的语句仅仅是 SQL 中可能实现的功能的一部分,但为了更好地理解本书的主要问题,这些语句为我们接下来的内容提供了足够的基础。
除了语句外,SQL 还提供了广泛的子句,可以用来整合命令,以精细化数据查询,并在数据库操作中加入约束条件。我们已经看到过用于 SELECT
语句的 WHERE
、DISTINCT
和 ORDER BY
子句。更高级的例子将在接下来的章节中探讨。
SQL 的安全性影响
正如我们所看到的,SQL 允许我们执行大量的指令,使得在许多不同层面与整个数据库进行交互成为可能。我们也可以通过修改数据库的结构来实现这一点。如此强大的语言可以用来对数据库执行各种操作,当然,我们会开始想,会有什么问题呢? 在大量可能的语句和操作中,恶意攻击者显然可以选择多种工具来以不同的方式破坏数据库、存储的数据以及使用这些数据的应用程序。例如,像DROP DATABASE <数据库名称> 这样的简单指令,可能完全破坏一个依赖数据库查询数据或甚至身份验证数据(即用户名和密码)的应用程序的功能。
因此,SQL 代码从来不是(至少直接)为了在应用程序中进行交互而设计的。相反,正是应用程序根据用户输入,准备好所需的 SQL 代码并发送到数据库,以提取(或修改)请求的数据。
然而,潜在攻击者仍然可以滥用 SQL 语法并插入任意指令。这种攻击方式通常称为代码注入,它涉及将计算机或系统识别的语言代码插入现有代码中,从而使得执行原本无法预见的任务成为可能。
作为一种简单(但非常强大)的语言,在 SQL 语句中注入代码相对容易,并且可能产生非常严重的后果,从授予任何人经过身份验证的访问权限,到完全破坏依赖数据库的 Web 应用程序。前面的例子只是可以被注入的许多破坏性命令之一。
使用 SQL 的主要问题在于代码在应用程序运行时被评估:如果没有控制措施,已经启动的程序本身不会从内容或正确性方面评估语句。恶意攻击者可以利用这一点,在用户提供的输入中插入任意命令,例如在身份验证表单或字符串字段中,这些输入会被应用程序评估,并插入到正在运行的代码中。
在接下来的部分中,我们将看到在一个存在漏洞的应用程序中,如何实现这一点。
使用 SQL 的弱点
导致代码注入——显然也包括 SQL 注入——的主要问题在于编程(和查询)语言本身固有的工作方式。
由于命令仅仅是作为代码解释的字符字符串,而用户输入是由文本组成的,我们原则上可以在用户输入中插入代码语法。如果没有正确验证并简单地接受输入,而没有我们进行任何控制,这些注入的代码可能导致恶意用户插入的任意命令被执行。
这是因为一个天真的字符串读取器不会区分文本和代码,因为它本质上是作为文本编码的二进制数据 —— 从计算机程序或应用程序的角度来看也是如此。通常,为了注入特定的指令或代码对象,使用特定的字符来欺骗解析器 —— 负责读取文本输入的软件组件 —— 使其将插入的代码解释为未预期的命令。传统上,注入代码的最简单方式是插入行终止字符 —— 在大多数编程语言中是分号 —— 这样,除了预期的操作之外,新的操作也会被视为完全不同的指令。其他字符可以用来操控应用程序的行为,例如注释分隔符,它用于完全排除指令后面的代码部分。
SQL 也不例外:许多用于代码注入的技术同样适用于 SQL。事实上,这个漏洞早在 20 多年前就被发现,当时是通过向 SQL 查询中注入命令,导致了未预期的操作。在后续章节中,我们将看到这种利用的具体形式,这些形式都可以用来对应用程序造成损害,或者帮助攻击者获得战略优势,既可以是数据方面,也可以是在某些情况下访问本应受限的系统。
幸运的是,SQL 注入只适用于编码不当的应用程序。对用户提供的输入和内部应用流添加特定的控制措施,可以彻底避免这个问题。除了改善输入的安全控制外,丢弃可疑的 Web 流量也有助于避免漏洞的利用。理想情况下,作为一个编码错误,你应该根据最佳实践开发安全代码。以下是一些将在本书后续章节中进一步探讨的建议:
-
不要允许查询中出现不必要的特殊字符:通常,SQL 注入是通过使用特殊字符来实现的。如果查询中允许特殊字符,那么这些字符也可以以一种 SQL 无法解释的方式进行编码,从而使基于特殊字符(如字符串分隔符(单引号或双引号)、指令分隔符(分号)和注释分隔符)的 SQL 注入攻击失败。
-
不要允许特定的可疑命令:一些命令经常用于 SQL 注入攻击。通过白名单仅允许特定的授权命令,有助于避免在应用程序中插入任意命令,从而符合软件组件的预期行为。
-
不要给予用户无限制的权限:虽然我们希望用户能够保持尊重和负责的态度,但对我们来说,用户可能是任何人——甚至是恶意用户。从这个角度来看,限制用户行为是一个好主意,因此我们永远不应该信任用户输入。查询输入应始终转换为参数并进行序列化。
这些要点有助于防范 SQL 注入,至少可以作为指导方针。本书后续章节将详细探讨如何防范 SQL 注入的低级和具体方法。一般来说,通过在应用程序编码中采用以安全为导向的方法,绝大多数漏洞和安全问题都能够得到解决。此外,在开发过程中加入安全控制可以节省时间和精力,因为重新编写代码要比从头开始编写带有这些控制的代码要困难得多。
SQL 注入 - 回顾
本章作为 SQL 注入基本概念的介绍,提供了一个概览。以下是对本章要点的总结,帮助你记住我们至今提到的主要概念:
- SQL 注入是 SQL 的一种软件弱点,SQL 是一种特定的语言和引擎,用于与基于关系模型的数据库结构进行交互,并通过表格以结构化格式处理数据。它允许恶意用户执行任意命令,从而以应用程序原本不打算的方式与应用程序的数据库交互。攻击者可以通过多种方式使用 SQL 注入:
-
SQL 提供了一种简单的语言,可以用于对关系数据库进行操作。在大多数情况下,SQL 语句的结构很简单。以下是一些 SQL 语句:
-SELECT
,用于从数据库中提取信息并返回记录-INSERT
,用于在数据库中插入记录-CREATE
,用于创建数据表或数据库-DROP
,用于删除整个表格或数据库-DELETE
,用于删除数据库中的记录-ALTER
,用于修改数据库或表格其中一些语句在注入时比其他语句更有效,但最重要的是
SELECT
,因为它是注入的主要推动力,也是应用程序中最常用的命令。几乎所有的 SQL 注入都通过在SELECT
语句中插入 SQL 命令来实现。一些特定的高级 SQL 命令还可以基于某些共同属性,将不同的表格建立关联。这是关系模型的主要优点之一,它能够将数据记录分隔到不同的表中来描述关系,同时这些表格又可以相互连接,扩大了可执行操作的范围。
-
基于 SQL 的系统在实现上可能有所不同,并且在语法(注释)和结构(默认数据库名称)方面可能存在一些小的差异:
-
MySQL 是一种开源实现,是最流行的版本。它可以被视为参考的基本 SQL 实现。
-
SQLite 设计为 SQL 的独立版本,将数据库存储在与正在运行的客户端应用程序相同的文件系统中。它使用基本的 SQL 语法,没有重大差异。
-
Oracle 数据库是企业环境中最受欢迎的 SQL 数据库系统之一,因为它还支持其他数据库模型,使其成为一个多模型系统。
- Microsoft SQL** (
MSSQL**) 服务器是另一个在企业环境中流行的解决方案,得益于它与 Microsoft Windows 环境的集成,而 Windows 环境在 IT 领域非常普及。一般来说,底层引擎在查询处理方面的工作方式相同,但在语言和默认数据库架构方面存在一些小的差异。
为了进行这次回顾,我们准备了一张参考表,突出显示了主要 SQL 引擎之间的一些基本差异。这将帮助你记住我们在本章中描述的主要差异:
-
SQL 在编码应用程序中用于与数据库进行交互,可以用于存储和访问数据,同时还提供获得认证访问权限的手段。数据库通常存储访问信息,如用户名和密码,并在数据库的特定表中进行匹配评估。因此,作为应用程序的组成部分,SQL 可能会被潜在的恶意行为者利用,他们可能会滥用其表达能力。
-
如果没有安全控制措施,应用程序将评估发送给它的每一个可能的文本字符串,从而可能允许恶意用户插入原本不打算执行的任意命令。为了抵抗潜在恶意用户的行为,最好的解决方案是实施安全驱动的开发方法,确保通过安全控制来防止 SQL 注入(以及其他许多攻击),这些安全控制应当遵循现有的安全编码最佳实践作为安全设计模式。作为一般蓝图,我们可以将一些安全设计原则总结为三大要点:
-
在查询中不要允许不必要的特殊字符,以防 SQL 语法被滥用。
-
通过仅允许特定指令,避免在查询中允许可疑的命令。
-
不要给予用户过多自由,从而防止恶意用户注入任意代码。
-
我们将在后续章节中看到更具体的安全原则,既包括防范措施,也包括现有的最佳实践,涉及应用程序安全。
总结
总结一下我们在本章中讨论的内容,SQL 通过关系进行工作,接受多种命令。我们还看到,一般来说,这些命令中的一些可能会被恶意攻击者滥用。因此,在设计和开发依赖数据库的应用程序时,我们应始终考虑安全性。本章让你了解了主要的安全问题及可能的解决方案。
下一章将重点讨论恶意攻击者如何利用 SQL 功能进行攻击。我们将提供一些示例,同时涉及与非关系型数据库相关的内容。
本章尽管较为抽象,但对理解 SQL 注入背后的核心概念至关重要。当然,这只是我们将在全书中深入探讨的内容的引言,届时会有更多具体的例子。在处理 SQL 注入的实际问题时,务必牢记这些主题。
我们对 SQL 注入的探索才刚刚开始!
问题
-
什么是数据库?
-
什么是关系型数据库?
-
什么是 SQL?它是用来做什么的?
-
能举出一些数据库系统中 SQL 实现的例子吗?
-
SELECT
在 SQL 中是什么意思?为什么它如此重要? -
你能用自己的话描述 SQL 注入吗?
第二章:操作 SQL – 利用 SQL 注入
在更一般地讨论结构化查询语言(SQL)及其特点和特性之后,我们现在将更多地聚焦于本书的核心内容:注入漏洞。我们已经在更广泛的意义上了解了 SQL 注入是什么,并且简要介绍了它可能带来的影响,以及为什么它是一个严重的安全问题。
在本章中,作为上一章的延续,我们继续采用理论方法,进一步接触 SQL 注入攻击的实际应用方面。本章实际上包括了触发 SQL 注入的输入字符串示例,这些示例可以用于多种不同的目的。
本章将为实践部分奠定基础,实践部分将专注于在受控环境中执行 SQL 注入攻击,将我们在本部分所学应用到实际操作中。
在讨论了 SQL 语法的 SQL 注入后,本章还将描述非关系型数据库中的注入漏洞。
本章将涵盖以下主题:
-
可利用的 SQL 命令与语法:我们将重点介绍最容易被滥用的 SQL 命令和语法结构,这些可能被用来实施 SQL 注入攻击。
-
常见的 SQL 注入命令与操作:概述主要的 SQL 攻击技术,展示攻击者使用的实际命令及其对应用程序或系统可能产生的影响。
-
不仅仅是 SQL 注入:非关系型数据库:简要介绍非关系型数据库的背景。
-
非关系型数据库中的注入漏洞:解释 SQL 注入在非关系型环境中的影响,展示一些可能的攻击技术。
-
总结:(非)SQL 注入理论:对本书理论部分的最终回顾,以巩固主要主题,并以更大的信心进入实践部分。
技术要求
对于本章,我们建议你熟悉 SQL 及其主要命令。如果你还没有这样做,建议先阅读上一章的 SQL 技术文档,文档可以通过以下链接获取:
-
docs.oracle.com/en/database/oracle/oracle-database/index.html
-
docs.microsoft.com/en-us/sql/sql-server/?view=sql-server-ver15
可利用的 SQL 命令和语法
我们已经看到,使应用程序和系统容易受到 SQL 注入攻击的主要问题是缺乏对用户输入的控制。默认情况下,输入源需要被视为不可信,因此必须在处理之前验证所有发送到应用程序或系统的内容。你现在可能会问:恶意用户到底是如何在我们的代码中插入指令的? SQL 是一种非常强大的语言,允许对数据库执行多种操作;如果应用程序被欺骗执行任意命令,可能会使想要破坏或访问系统的人获得严重的优势。在本节中,我们将看到 SQL 注入的主要促成因素,并强调正确预处理输入的重要性,从而避免通过一个简单的输入字符串让我们的系统和应用程序遭受攻击。
启用 SQL 注入的字符
在处理 SQL 语句和构造之前,我们应首先检查在缺乏输入控制的情况下,为什么能够插入任意指令。
与大多数机器解释语言中的情况类似,SQL 将一些特定字符映射到代码文本中的相应用途。一个人最容易尝试利用的字符是单引号(')或双引号("),因为它们在查询中作为文本值的定界符。
一个例子是分号字符(;),它用于分隔不同的命令(与大多数编程语言相同)。
另一个在 SQL 注入中被广泛滥用的字符是注释分隔符,在大多数语法中,它对应于–序列,因为它可以使 SQL 查询的下一部分变得无效,系统会将其视为注释。
作为一个普遍的例子,考虑一个用于查询物体颜色的简单SELECT
语句,普通用户会输入red
,如以下所示:
SELECT color, shape FROM objects WHERE color='red'
如果插入的文本输入不是red
,而是以下内容,情况可能会大不相同:
red'; DROP TABLE objects --
这将导致查询看起来像这样:
SELECT color, shape FROM objects WHERE color='red'; DROP TABLE objects –-'
用户提供的输入未经过清理,可能会导致系统在处理使用预期语法的命令时,错误地执行另一个 SQL 命令,完全删除数据库中的objects
表。添加注释分隔符会自动移除应用程序插入的第二个单引号字符,从而使恶意用户能够在保持语法正确的情况下插入他们想要的任何 SQL 命令。
为了更好地理解在输入未被检查的情况下可能发生的操控程度,我们将看到 SQL 语句通常是如何在应用程序中构造的。
SQL 语句构造
如前所述,应用程序中最常用的 SQL 语句是 SELECT
语句。很多时候,当你遇到一个用于搜索项的 Web 表单时,它会与应用程序中的数据库连接,从而以结构化和可靠的方式访问数据。
想象一个常规的登录表单,由给定的用户名和密码文本输入框组成。应用程序的作用是匹配插入的信息,查看提供的用户名和密码对是否存在于数据库中的同一条记录中(与单个用户关联)。因此,如果结果存在,应用程序就知道该用户有权限访问,并授予必要的权限。
如果我们将 Overlord
作为用户名,pass
作为密码,那么生成的 SELECT
语句将如下所示:
SELECT * FROM users WHERE username='Overlord' AND password='pass'
应用程序将收集到的字符串,存储为变量,作为文本插入到构成查询主体的另一个文本字符串中。当然,这些变量可以通过各种方式在 Web 环境中获取(作为 超文本传输协议(HTTP)GET
请求的参数——我们不推荐这样做:现在已经不是 90 年代了——或作为 POST
请求)。为了简便起见,在本例中,我们将考虑通过 GET
请求从(虚构的)地址 sqlexample.com/login.php?username=Overlord&password=pass 获取变量。
因此,应用程序的代码可能如下所示 PHP: 超文本预处理器(PHP)示例:
\(user=\)_GET[username]; //$_GET 从参数中提取数据 \(pass=\)_GET[password]; //从地址中提取数据(在“?”之后) $query="SELECT * FROM users WHERE username='" + $user +"' AND password='" + $pass + "'";
通过这种方式构造查询,可以清楚地看到如何利用用户提供的输入来修改语句,如前所述。这也是为什么在处理用户提供的输入时要小心的重要原因,不仅是在使用 SQL 时,而是一般来说,因为我们不能假设任何人都有良好的意图。
我们现在将分析一些 SQL 注入命令的示例及其攻击者的目的。
常见的 SQL 注入命令和操作
SQL 注入可以以多种不同的方式用于许多不同的目的,因为 SQL 可以执行许多不同的操作。最简单的使用方法是试图获取通常无法访问的信息,通过数据库查询以应用程序逻辑中通常未考虑到的方式。其他用途包括绕过应用程序中的身份验证门,从而有效地提升权限,或者在存储凭证的情况下,获得更多对受影响系统的控制。其他常见的攻击包括盲注 SQL 注入:在大多数情况下,数据库控制台或输出不会显示给攻击者,因为操作发生在所谓的前端背后;然而,攻击者可以通过观察应用程序的行为来识别并利用 SQL 注入。我们将看到一些著名的攻击技术示例。
信息收集与模式提取 – UNION 查询
任何攻击的第 0 步都是收集有用的信息,以便收集足够的数据来识别目标,例如系统配置、可能的内在漏洞和攻击点。虽然严格来说这不是 SQL 注入,但使用 SQL 收集信息本身就是一种攻击形式,这对于需要在系统上利用 SQL 注入的攻击者来说,尤其是考虑到主要 SQL 数据库管理系统(DBMSs)之间的细微差异,显然是非常有用的。
了解目标系统的最简单方法是通过触发错误信息,如下所示:
图 2.1 – 测试应用程序因 SQL 语法错误而产生的错误信息
在这种情况下,通过输入故意错误的语法(即,我们在表单的用户名字段中插入了一个错误的字符串),我们得到了一个有用的错误信息,指出我们正在处理的是一个 MySQL 数据库。如今,只有编码不良的应用程序才会在语法错误时显示错误信息,但尝试一下绝对是值得的。
继续我们对可用工具的探索,我们现在将重点讨论 SQL 注入中经常使用的一个高级 SQL 命令:UNION
。
UNION
命令是通过 SQL 注入进行数据库发现和转储的最强大工具之一。从逻辑上讲,它用于将两个或更多查询的结果合并到同一个结果表中。我们可以参考以下来自*第一章**的示例,《SQL 注入的结构化查询语言》:
SELECT color, shape FROM objects WHERE color='blue' UNION SELECT color, shape FROM objects WHERE color='red'
上一个查询显示了color
和shape
属性的记录,这些记录来自objects
表,并且它们在 color 属性上有blue
的值,同时也把颜色为red
的记录放入了相同的结果表中。请记住,UNION
仅在两个查询的属性维度相同时才有效。还可以在UNION
部分插入任意值,像这样:
SELECT color, shape FROM objects WHERE color='blue' UNION SELECT 1,2
在这个例子中,我们在与第一个查询相同的输出表中显示了任意值1
和2
。此外,这个技巧还可以用来显示任意值作为输出,并且使UNION
部分与前一个查询的格式一致,可能会显示隐藏的信息。
你还记得上一章的@@VERSION
命令吗?这个有用的命令可以被攻击者用来查看正在运行的数据库版本。SELECT @@VERSION
查询实际上显示的是目标系统的版本。让我们通过一个例子来看一下,内容如下:
图 2.2 – 一个 UNION 查询的结果,显示了查询之后要插入的数据库系统版本
尽管这个命令在 MS SQL 的情况下最为有用,因为它还可能显示有关 Windows 操作系统的重要漏洞的相关信息,但它也能报告关于其他系统的一些信息(注意:@@VERSION
在 Oracle 数据库中不受支持)。这个例子来自开放 Web 应用程序安全项目(OWASP)的另一个故意易受攻击的 Web 应用程序(Vicnum)。报告的版本是5.1.41-3ubuntu12.6-log
,表明这是一个安装在 Ubuntu 12 上的 MySQL。
通过 SQL 注入进行信息收集的另一个重要组成部分是枚举模式中包含的表和数据库。再次强调,UNION
命令将非常有用,因为它提供了足够的灵活性。
我们利用上一章中显示的默认表来进行实验。让我们尝试显示数据库中所有可用的模式。为了示范,我们再次以OWASP Vicnum
为例。请看以下截图:
图 2.3 – 一个 UNION 查询的结果,显示了数据库中的模式名称
注意我们可以看到同一系统中的多个模式吗?这是因为我们的目标位于一个模拟系统上,该系统上有多个应用程序。你可以想象,针对一个共享数据库中的应用程序进行攻击,可能会揭示大量信息,不仅是目标应用的,还包括整个系统的。为了让你更有兴趣:这个模拟环境将是你在第三章中能够设置的环境,设置环境,它将成为我们实际操作部分的主要目标之一。
我们坚持使用information_schema
默认表,因为它包含关于 MySQL 系统中模式组织的所有信息。前面的一个结果是与 WordPress 应用相关的模式,因此我们将尝试通过另一个UNION
关键字注入此查询,以显示目标模式中的所有表,如下所示:
SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'wordpress'
这样查询的结果,如果插入到UNION
关键字之后,将在一个脆弱的应用程序中显示以下结果,该应用程序公开显示查询结果:
](image/B15632_02_004.jpg)
图 2.4 – 显示目标模式中的模式和表名的 UNION 查询结果
这可以对数据库中找到的所有模式进行处理。我们已经看到过使用 MySQL 默认表的这些信息收集技术,但现在让我们也考虑一下另外两个具有客户端-服务器架构的主要数据库管理系统。它们各自有一些独特之处,相对于 MySQL 引入了一些不同。
Microsoft SQL Server
正如我们在第一章**,SQL 注入的结构化查询语言中所提到的,Microsoft SQL Server 也有一些默认的表和数据库。其中一个对攻击者非常有帮助的数据库是名为master
的数据库,它包含有关整个数据库系统的信息。与我们对 MySQL 所做的类似,通过查询sysdatabases
表,可以获取所有数据库的列表,具体如下:
SELECT name FROM master..sysdatabases
这个查询与我们最初在 MySQL 中执行的SELECT schema_name FROM information_schema.schemata
语句完全相同。通过UNION
查询,可以扩展信息收集,查询数据库中的表,借助sysobjects
表显示其中的元素,具体如下:
SELECT name FROM databasename..sysobjects
这个查询会显示很多信息,包括噪声。幸运的是,可以通过关注特定类型的数据来精炼搜索。例如,选择xtype
字段并设为U
值,将只过滤出用户定义的表。以下是sysobjects
中xtype
字段可能的值列表:
-
C
:CHECK 约束 -
D
:默认或 DEFAULT 约束 -
F
:外键约束 -
L
:日志 -
P
:存储过程 -
PK
:主键约束(类型是 K) -
RF
:复制过滤存储过程 -
S
:系统表 -
TR
:触发器 -
U
:用户表 -
UQ
:唯一约束(类型是 K) -
V
:视图 -
X
:扩展存储过程
Oracle 数据库
至于 Oracle 数据库,尽管它也有默认的表和数据库,但与 MySQL 和 Microsoft SQL Server 相比,结果可能会有所限制,因为枚举(如我们之前所见)由于其结构不完全可能。然而,不必担心:即使有访问权限,Oracle 数据库仍能提取许多信息。
由于 Oracle 数据库的分区特性,数据库名称只能在特定上下文中获取。为了返回当前数据库,攻击者可以尝试以下几种查询:
SELECT name FROM v$database;
该查询将返回存储在v$database
中的名称,v$database
存储关于——你猜对了——数据库的信息,如下所示:
SELECT global_name FROM global_name
global_name
是一个单行表,存储当前数据库的名称,类似于:
SELECT SYS.DATABASE_NAME FROM DUAL
DUAL
表是一个默认表,用作虚拟表:它只包含一个值,设置为x
。由于它对任何用户都可见,因此通常在计算常量表达式时使用。在这种情况下,SYS.DATABASE_NAME
并未与DUAL
表连接,但它是一个常量。
在 Oracle 中,用户访问信息的权限取决于权限设置。以下查询返回当前用户可访问的所有表:
SELECT table_name,owner FROM all_tables;
要检索所有可用列,可以使用以下查询:
SELECT column_name FROM all_tab_columns
当然,由于它返回的结果数量可能非常庞大,最好精细化查询(例如,使用WHERE
或LIKE
,后者是WHERE
的一种宽松方式)。
攻击者可以识别出包含私密信息的有趣表格。说到这个…让我们进入下一小节!这一小节将更多地关注 MySQL,因其有一些有趣的示例,但这种推理也适用于其他数据库管理系统,除了我们之前提到的注意事项。
导出数据库
使用我们从数据库架构中提取的信息,我们可以查看从可注入数据库中提取的所有信息。再一次,UNION
帮助了我们,这次使我们能够深入提取我们需要的任何表的完整内容。
这个思路是对表中的字段进行枚举,然后采用相同的方法,在发现数据库完整架构后提取我们所需的所有内容。如果数据库存在 SQL 注入漏洞,攻击者可以完全访问整个数据库,并提取其中包含的敏感信息。
为了解释的目的,我们现在针对前一个例子中的wp_users
表。我们现在感兴趣的是获取该表的完整结构,列出其字段。攻击者可以利用这个方法探索数据库并检测潜在有用的信息。我们现在需要在UNION
后插入的查询语句类似于以下内容:
SELECT table_schema,table_name,column_name FROM information_schema.columns WHERE table_name = 'wp_users'
通过执行如前一个例子中的UNION
查询,插入缺失的值,使得我们的列与原始查询相同,我们现在可以访问所选表的字段名,如下图所示:
图 2.5 – 显示目标字段名称的 UNION 查询结果
噢,这有点尴尬:我们找到了user_login
和user_pass
字段,显然这些字段包含了登录信息。让我们尝试使用之前收集到的信息,进行一个简单的查询,如下所示:
SELECT ID,display_name,user_login,user_pass FROM wordpress.wp_users
这将产生以下响应,向我们提供关于依赖于数据库的 WordPress 实例中用户配置文件的信息:
图 2.6 – 显示 wp_users 表的 UNION 查询结果
作为一种常见做法,密码会进行哈希处理。这意味着这些密码无法使用,除非被破解。在这种情况下,我们有 MD5 哈希值,这些哈希值可以被专门的软件轻松破解。这意味着攻击者可以轻松地获取这些账户的登录信息。
这种深入查询的方法原则上可以用来获取攻击者希望从数据库中获得的所有信息。默认数据库,如 MySQL 的information_schema
数据库,可能导致获取数据库系统中所有信息的完整映射。
提升权限并获得访问权限
现在让我们继续探讨 SQL 注入攻击的另一个目的。我们已经在第一章中提到过,SQL 注入的结构化查询语言,SQL 如何用于提升权限或访问应用程序和/或系统。接下来我们将深入讨论这一方面。
数据库通常用于身份验证目的:每当你将登录信息输入到网页表单时,在大多数情况下,这些数据会与存储在特定数据库中的信息进行比较。通过这种方式,系统可以知道你是否有权限通过应用程序的身份验证门禁。我知道你在想什么:既然我们正在与数据库交互,那么有人肯定会尝试进行注入攻击,对吧?完全正确。一个容易受到 SQL 注入攻击的应用程序,实际上可能允许攻击者获得比预期更多的权限。
记得前一小节中的示例吗?我们在数据库架构中获得了存储在 WordPress 实例的wp_login
表中的信息?这里再次为您参考:
图 2.7 – 来自 WordPress wp_login 表的记录,对应于管理员用户
出于安全目的,为了防止通过数据库转储进行密码攻击,密码不会以明文形式存储在数据库中。数据库中存储的是密码的 MD5 哈希值,仍然可以用于与应用程序进行比对。MD5 是一种哈希函数,它会生成一个特定的消息摘要,由 128 位表示,并以 32 个十六进制数字(0-9,a-f)表示给定输入。由于 MD5 现在被认为过于脆弱,这种哈希函数已经被更复杂且安全的哈希函数(如 SHA-256)所取代。在这种情况下,密码本身非常容易预测,而且可以在相对较短的时间内获取。该哈希值对应于管理员密码。让我们在本地模拟环境中的 WordPress 应用程序中尝试一下。如下图所示:
图 2.8 – 成功尝试验证 WordPress 管理员账户
另一种通过 SQL 注入获得访问权限的方式是完全绕过易受 SQL 注入攻击的身份验证表单。最臭名昭著的 SQL 注入身份验证绕过示例利用了这样一个事实:通常,用于身份验证的 SQL 查询仅仅依赖于满足条件的记录是否存在于数据库中。因此,我们的最终结果需要为“真”。因此,我们谈论的是重言式。
在布尔逻辑中,重言式是一个总为真的逻辑表达式,无论条件如何。在二进制OR
运算中,若两个操作数中的任意一个为真,结果将为真,这意味着结果始终为TRUE
。
这同样适用于 SQL 注入:SQL 也支持用于条件的布尔运算符,我们总是在WHERE
部分的SELECT
语句中看到它们。例如,如果我们编写这个 SQL 查询,我们将总是满足WHERE
条件:
SELECT * FROM table1 WHERE field='x' OR '1=1'
这是攻击者原则上可以用来绕过身份验证的最基本查询之一。因此,攻击者可以在易受 SQL 注入攻击的登录表单中插入以下字符串——例如,在用户文本输入框中:
x' OR '1=1'--
通过使语句始终为真,攻击者可以绕过易受 SQL 注入攻击的应用程序的身份验证。如我们在之前的示例中看到的,注释掉查询中的以下部分有助于让系统仅评估我们想要的内容。
根据底层查询的不同,应该考虑一些额外的因素。例如,攻击者应当知道数据库运行的是哪种数据库管理系统(DBMS),以便选择正确的字符作为注入启用符号。其他登录表单可能会检查用户名和密码两个文本输入框是否为空,因此攻击者应在两个输入框中插入信息。
最终,排除试错过程,SQL 注入原则上可以允许攻击者绕过认证屏幕,并获得比预期更高的权限。
我们现在将分析其他常见的攻击技术——这次,可能是最广泛使用的一种。
盲 SQL 注入
大多数时候,与数据库的交互不会提供记录输出——与我们在 Vicnum 示例中看到的不同。因此,攻击者无法直接通过记录或表格反馈他们在数据库上执行的操作。
在这种情况下,我们谈论的是盲 SQL 注入,因为攻击者与数据库交互时,至少从直接上来说,看不到他们的操作结果。当然,认证绕过就是盲 SQL 注入的一个例子,但这并不是唯一的。
盲注 SQL 注入是通过所谓的推断攻击来揭示信息。基本上,它通过各种尝试,基于网站响应的逻辑假设,揭示关于数据库的信息。在重言式和矛盾中,我们选择使我们的语句有可预测的结果——始终为真或始终为假——而这次,我们将使用可能为真的条件,如果它们为真,它们可以揭示一些信息。
即使看不到数据库的输出,攻击者仍然有一些方法来判断应用程序是否易受 SQL 注入攻击。在盲注场景中,最常见的测试 SQL 注入的方法之一是通过在查询提交中引入一个任意的时间延迟。
查看与数据库交互的应用程序是否具备可注入性的一种常见方式是使用逻辑表达式,类似于我们在重言式中看到的方式。在某些情况下,根据应用程序可能返回的响应,可以通过对其如何处理逻辑表达式的假设来判断它是否可注入,甚至泄漏一些信息。在这里,我们处理的是基于布尔值的盲注。
如果我们尝试一个恒真式(例如,' OR '1=1'--),然后再尝试一个恒假表达式——即矛盾——通过使用AND
操作符(' AND '1=2'--),我们可能会看到响应外观上的不同结果。在这种情况下,我们可能就会得到发现 SQL 注入的线索:我们注入的 SQL 片段成功执行,改变了查询结果。在数据库结果方面,第一个尝试确保如果成功评估,所有结果都会返回,因为条件始终满足;反之,第二个则会返回空结果。
这个技巧是,通过了解真和假结果之间的输出差异,我们可以通过将逻辑语句放入AND
操作中来判断其真假。这样,我们就可以通过这种比较来调查数据库信息,因为我们不能像非盲 SQL 注入那样直接查询数据库。一个有用的技巧,除了将整个字符串与字段值进行比较外,就是使用SUBSTRING()
函数检查特定位置的特定字符,从而重构我们需要的信息。如果我们要提取字符串字段值的第一个字母,我们可以插入如下条件:
SUBSTRING(fieldname,1,1)= 'x'
当然,这个过程可以反复进行,以获得我们所需的所有信息,但手动执行时肯定会很麻烦。攻击者可能会使用一些脚本来自动化这个过程。
执行盲 SQL 注入的另一种方式是通过使用基于时间的 SQL 注入。有时,真或假的结果输出差异不大,因此攻击者需要引入一些人为的差异。这是通过一些主流 SQL 数据库系统支持的巧妙函数实现的。
MySQL 支持SLEEP()
和BENCHMARK(count, expression)
函数,它们可以集成到任何语句中。例如,以下代码片段在查询中插入了15
秒的时间延迟:
SLEEP(15)
这个方法则是执行SELECT @@VERSION
查询10000
次,根据执行时间引入间接的时间延迟,代码如下:
BENCHMARK(10000, SELECT @@VERSION)
Microsoft SQL Server 则支持WAITFOR DELAY()
和WAITFOR TIME()
函数。与之前的SLEEP()
函数相同的结果可以通过以下代码片段获得。DELAY
引入时间延迟(相对的),而TIME
指定等待结束的实际时间。以下例子中,我们假设当前时间是 9:00:
WAITFOR DELAY(0:0:15)
WAITFOR TIME(9:0:15)
Oracle SQL 执行基于时间的查询的方式稍微复杂一些。实际上是有SLEEP()
函数的,但它只能在 Oracle SQL 编程代码中插入,因为常规动态查询不支持。代码片段应如下所示:
BEGIN DBMS_LOCK.SLEEP(15); END;
然而,确实有一些技巧可以引入时间延迟。这是通过消耗时间的查询实现的,包括依赖网络的任务(例如反向域名系统(DNS)查询)或使用多个(或复制的)表进行数据查询。以下代码片段是这类查询的两个示例,可以被注入。然而,它们的有效性可能会根据目标的不同而变化:
SELECT UTL_INADDR.get_host_name('10.10.10.10') FROM dual
SELECT count(*) FROM all_users A, all_users B, all_users C, all_users D # 依此类推…
通过在发出请求后验证时间延迟,攻击者可以看到后台系统是否在评估函数,从而确认 SQL 指令可以成功注入。
时间延迟和布尔查询也可以结合使用:没有人说我们不能利用时间延迟来判断条件是否为真。事实上,以下查询也是合法的:
SELECT IF SUBSTRING(fieldname,1,1)='x' SLEEP(15)
这样,我们的信号就是通过响应前的时间延迟来传递,而不是通过使用逻辑条件。
在盲注 SQL 注入的范畴内,另一个重要的技巧叫做 拆分和平衡。其主要思路是尝试那些根据 SQL 功能上相同的查询,并确保查询中括号和引号的开闭是完全平衡的,从而生成合法的 SQL。我们可以考虑以下两个非常基本的 SELECT
查询:
SELECT name FROM customers WHERE id=3
SELECT name FROM customers WHERE id=2+1
这两个查询在功能上是完全相同的,因为涉及到的算术是显而易见的。这个方法也可以用在字符串数据上,执行可能的字符串操作,如连接(||),如果数据库管理系统允许的话,如下代码片段所示:
SELECT name FROM customers WHERE name='Jonathan'
SELECT name FROM customers WHERE name='Jo'||'nathan'
这里的关键是:通过使用等效查询,其他查询也可能被注入,如下所示:
SELECT name FROM customers WHERE id=3
SELECT name FROM customers WHERE id=3+(SELECT 2-2)
通过利用 SQL 提供的这一可能性,更多复杂的子查询也可以插入括号之间,可能插入有害的攻击负载。
我们现在已经看到了 SQL 注入攻击针对 SQL 系统的主要示例概述——没有示例。接下来让我们讨论一个通常未被考虑的方面:SQL 注入是否也适用于 NoSQL?从名字上看,你可能会认为它不适用,但现实比这个误导性的名字要复杂得多。
不仅仅是 SQL 注入——非关系型数据存储
NoSQL这个术语多年来一直备受争议。有些人,可能是没有足够注意的人,会告诉你它意味着没有 SQL,也就是这件事完全与 SQL 无关。虽然确实这些数据库使用的方式不同于关系模型(正如我们在第一章中所见,SQL 注入的结构化查询语言),但是一些底层逻辑是共享的。NoSQL 这个术语的产生是为了强调它与主流数据库模型的差异。进一步说,由于 NoSQL 这个术语会引发一些误解,它已经不那么受欢迎,取而代之的是更通用的术语非关系型,简称no-rel
。
正如我们在第一章中提到的,SQL 注入的结构化查询语言,SQL 注入的原理在某种形式上也会影响那些不包含 SQL 或关系模型的数据库。一个简单的解释是,注入的原理,正如代码注入一样,适用于任何负责解释某段代码的软件。
非关系型数据库开发者的一项主张是,通过不使用标准字符串来构建实际的查询,非 SQL 数据库不容易受到注入攻击。可惜,实际上也有出现这种漏洞的例子,主要是在 MongoDB 等基于文档的数据库中。
简而言之:仅仅因为你的数据库不是基于 SQL 的,并不意味着它免疫于注入攻击。虽然这被称为SQL 注入,但这只是因为它是在 SQL 环境中发现的。更重要的是,这一点也并不意味着只有 SQL 数据库系统容易受到注入攻击。
非关系型存储库中的注入漏洞
注入性问题严格依赖于对输入的信任,这些输入可能包含可解释的代码。对于某些 NoSQL 数据库系统,这一点也同样适用。
基于文档的数据库仍然使用格式化文本以结构化的方式插入数据。大多数使用这种数据库的应用程序主要依赖文本,无论是JavaScript 对象表示法(JSON)格式,还是来自用户提供的输入。因此,如果没有适当的清理,特定的输入可能会触发一些问题,类似于 SQL 中的情况。
假设我们现在考虑一个虚构的网站,该网站依赖基于文档的数据库 MongoDB 进行身份验证。攻击者可能会发送一个 HTTP GET
请求,https://targetsite.org/login?user=admin&password[%24ne]=
。该目标网站使用Node.js
框架编写,遗憾的是,它检查凭证的方法非常简单。请看以下代码片段:
db.collection('users').find({
"user": req.query.user,
"password": req.query.password
});
在这种格式下,网站仍然接受恶意内容,因此会授予恶意用户访问权限。为什么?因为无论采用什么技术,未经清理的输入仍然可能被插入。请求将会按如下方式被解释:
db.collection('users').find({
"user": "admin",
"password": {"$ne": ""}
});
$ne
在 MongoDB 中是一个特定的操作符,用于定义 不等于 关系。在实践中,它在 MongoDB 中是这样读取的,并使得 find()
函数成功执行,从而以类似于 SQL 注入的方式授予访问权限。这是因为 MongoDB 期望输入采用特定的字符串格式——即 JSON。
GET
示例仅用于说明目的,但此攻击也可以在 POST
请求中工作,如下方代码片段所示:
POST /login HTTP/1.1
Host: targetsite.org
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
user=admin&password[%24ne]=
作为纯文本,内容也可以按如下 JSON 格式请求编写:
POST /login HTTP/1.1
Host: targetsite.org
Content-Type: application/json
Content-Length: 36
{'user': 'admin', 'password': {'$ne': ''}}
通过这个示例,明显可以看到 SQL 注入的原理如何应用于非关系型数据库模型。当然,由于没有使用强大的查询语言编写查询,攻击的范围受到限制,信息收集和数据库导出变得不可能。然而,通过了解服务器端代码的语义,攻击者仍然可以利用这一点来获得自己的利益。
通常,攻击者可以在输入中插入改变查询语义的对象,从而导致意外的行为。解决方案始终是相同的:正确清理输入,并预期用户可能的攻击尝试。
在这个简短的示例之后,我们希望至少在理论上,SQL 注入及其在 SQL 范畴之外的应用已经清晰,并且你已经掌握了将知识付诸实践所需的工具。
总结——(非)SQL 注入的理论
好的,这信息量蛮大的。让我们回顾一下到目前为止在这个理论部分讨论的内容。
攻击者可以在各种场景下使用 SQL 注入。在本章中,我们已看到关于两种常见用途的示例,具体如下:
-
通过数据库探索或推理技术获取关于数据库或其内容的未公开信息
-
获取使用共享数据库系统的应用的特权访问权限
限制应用功能也可能通过 SQL 语句,如 DROP
,或通过修改数据库中的重要信息,如登录信息来实现。
在本章中,我们加入了另一个非常重要的工具,可以在 SQL 语句中使用,具体如下:
UNION
可以添加到现有语句中,以返回与同一结果表中另一个查询相关的结果。为了正常工作,第二个查询必须与第一个查询具有相同数量的字段,但这可以通过添加任意静态值(如固定数字)来轻松获得。
SQL 注入,特别是使用UNION
,可以用于信息收集。可以从一个脆弱的数据库中提取大量信息:
-
可以查询数据库架构以获取有关系统内的数据库、表和表字段的信息。
-
得到的信息可以直接用于查询数据库,精确知道需要提取哪些表和字段。
-
UNION
查询可以检索大量信息,特别是在 MySQL 和 MSSQL 中,许多数据库可以被查询,尤其是当系统运行着依赖于多个数据库的应用时。
基于 SQL 的系统由于实现的差异,可能会有一些细微的差别,如在第一章中所见,SQL 注入的结构化查询语言。以下是其中一些:
-
在默认数据库中,一些数据库在所包含的信息方面比其他的更有趣。
-
表可以通过不同方式访问——例如,MSSQL 使用
..
来访问我们默认数据库中的表。 -
Oracle 数据库在单个连接中访问单个数据库,因此攻击者每次只能检索一个数据库的信息。
我们准备了以下快速参考表,突出显示了主要数据库系统之间的一些基本差异,这在信息收集过程中,查询数据库和表以获取信息时,可能会非常有用:
SQL 注入还可以帮助攻击者获取权限并访问通常无法访问的应用功能,具体如下:
-
从数据库中提取信息有时可能导致密码泄露,因为密码哈希值存储在数据库中,如果使用了弱哈希算法,可能会通过离线密码攻击进行解密。
-
使用恒等式,也称为总为真的表达式(如1=1),可以使登录查询始终为真,从而在应用程序中的脆弱认证表单内获得访问权限。
一种最常见的 SQL 攻击技术叫做盲目 SQL 注入,因为大多数时候,攻击者无法直接访问数据库输出:
-
所有不涉及查看数据库输出的先前示例,包括认证绕过,实际上都是盲目 SQL 注入。
-
基于时间的 SQL 注入可以用来判断数据库是否可能受到 SQL 注入的攻击:攻击者在查询中插入时间延迟,并检查数据库系统是否正确解释了这一点。
-
基于布尔值的 SQL 注入利用逻辑语句重建隐藏的数据库信息,因为攻击者无法通过查询看到实际的数据库内容。攻击者通过观察应用程序在真假语句上下文中的行为来实现这一点。如果行为不同,攻击者可能会尝试注入条件并根据响应判断它们是否为真。
-
基于时间的查询和基于布尔值的查询可以结合使用:攻击者可能会通过
UNION
语句插入IF
条件,根据结果,可能会引起一定的时间延迟。这样,攻击者可以通过研究应用程序在响应时间方面的行为进行推理,而不是内容。 -
拆分和平衡是另一种盲目 SQL 注入技术,它滥用了某些查询的等价性,如果应用程序存在漏洞,甚至可能在某些情况下通过使用括号插入任意子查询并确保语法正确。
尽管被称为 SQL 注入,但这种漏洞对非关系型数据库也有影响:
-
虽然数据库并不总是依赖像 SQL 这样的强大查询语言,但这并不意味着不能注入任何命令或修改。
-
在 NoSQL 数据库的情况下,我们可以讨论 NoSQL 注入。虽然到目前为止我们见过的大量攻击通常无法执行,比如数据库倾倒和任意查询,但攻击者只要能访问一种在应用程序中插入输入的方式,就可以随意改变某些语义。
-
正如我们在登录绕过示例中看到的那样,NoSQL 数据库可以通过插入元素的简单方式进行修改,这些元素可以改变语法并欺骗底层数据库评估特定条件,从而可能导致有害行为。
-
虽然 SQL 注入可能更具危害性,但最好不要低估其他数据库环境中的注入漏洞:如果依赖数据库的应用程序没有对用户输入进行清理,它仍然可能受到注入攻击。
总结
总结一下,在本章中,我们看到 SQL 可以通过特定的构造和符号被利用来插入恶意代码。某些构造特别有助于收集信息,但也有助于获取应用程序和数据库本身的特权访问权限。
我们还看到,数据库系统中的注入概念不仅涉及 SQL 数据库,还包括一些非关系型数据库,我们也看到了一些例子。
下一章将是实践部分的第一章,重点介绍我们在涉及 Mutillidae II 和 Vicnum 的示例中所见的相同虚拟环境的设置(通过查询information_schema
数据库,你可能注意到了包括我们之前看到的易受攻击的 WordPress 版本在内的多个应用程序)。虽然本章中的实践示例仅起到说明作用,但本书的第二部分则旨在采用更为实际的方法,并以逐步指导的方式呈现。
本部分旨在为 SQL 注入主题提供全面的介绍。然而,理论知识并不足够:掌握一个主题需要实践,这也是本书后续实际部分的核心所在。
我们希望你喜欢我们为你准备的内容!
问题
-
通常如何触发 SQL 注入?
-
简要描述如何在一个易受 SQL 注入攻击的应用程序中提取数据库信息。
-
描述一个恶意用户如何利用 SQL 注入绕过用户认证并访问应用程序。
-
什么是盲 SQL 注入?描述执行盲 SQL 注入的两种方式。
-
你正在处理一个依赖数据库的应用程序。你怀疑一个网页表单依赖 SQL 数据库,但该应用程序在查询后未返回有意义的输出。你会使用哪种 SQL 注入技术来判断该应用程序表单是否易受 SQL 注入攻击?
-
是否只有 SQL 数据库容易受到注入攻击?
第三章:环境设置
在本章中,我们将介绍用于核心实践部分的测试环境的设置,同时定义本书实践部分背后的主要方法。将介绍的主要工具将帮助我们执行 SQL 注入测试,进一步帮助我们理解相关内容。
在介绍了主要方法论和工具之后,我们还将展示如何配置将要使用的实验室设置。本章将作为一步步的配置指南,帮助我们最佳配置客户端(我们将用来进行测试攻击的机器)和服务器端(由虚拟目标组成,我们将对其进行测试)。幸运的是,我们选择的工具大多数已经可以直接使用,设置也相对简单。
由于客户端部分在 Web 应用攻击和模拟设备攻击中大致相同,第一步将是配置这台机器。
本章将涵盖以下主题:
-
理解实用方法并介绍主要工具:本节将介绍我们的方法,并指导我们整个实践部分的内容。可以将其看作是实践部分的引言,描述我们将使用的实验室以及整体设置。
-
开放式 Web 应用安全项目(OWASP)破损 Web 应用(BWA)项目概览:对于我们的实验室设置,我们将使用由 OWASP 提供的免费虚拟机(VM),其中包含许多易受攻击的 Web 应用。这使我们能够在不攻击实际网站或第三方实体的情况下,测试漏洞。
-
攻击者 – 配置你的客户端机器:在为实验室设置目标之前,本节将提供我们对用于专门攻击的主要客户端机器的建议。
-
目标 – 配置你的目标网络应用:我们为设置目标网络服务器提供的指导。
-
目标 – 配置你的目标模拟设备:我们为物联网(IoT)测试配置目标设备提供的指导。
-
操作实验室:在本章的最后一节,我们将描述如何操作实验室,汇总我们已经看到的所有元素。
技术要求
尽管本章涉及的是严格的实践内容,但没有特别的前提要求。然而,我们建议熟悉我们将使用的主要工具。所有这些工具都是免费的,因此无需担心费用问题。以下是工具的链接:
理解实践方法并介绍主要工具
在理解攻击技术,尤其是漏洞和网络攻击时,最重要的方面之一是亲自探索进行攻击意味着什么,同时评估被攻击目标的行为,以找出可能的最佳防御解决方案。这是道德黑客的核心原则之一:从潜在攻击者的角度理解问题,意味着能更全面地了解发生了什么,这完全符合“了解你的敌人”的战略概念,从而最终获得战略优势。本节内容正是围绕这一点展开:在受控且安全的环境中进行攻击测试,亲身研究和体验 SQL 注入的本质。
为了达到这种专业水平,将使用一系列工具。SQL 注入大多是基于 Web 的漏洞,这意味着可以通过简单的 Web 浏览器进行利用,发送特定输入与应用程序进行交互,正如本书中的实践部分所示。当然,针对真实目标进行这些测试可能会非常棘手,因为这需要特定的协议和参与规则,以确保我们不会对公司或个人的系统造成伤害。在没有事先签署协议的情况下进行攻击是违法的,我们不希望也不鼓励任何人犯罪。因此,我们将介绍我们的第一款软件,它允许我们在自己的计算机上设置一个受控环境,而不攻击他人:虚拟化软件。
虚拟化软件
我们工具集中的第一项是虚拟化软件。虚拟化是通过软件完全仿真硬件资源(即计算机系统)的过程。仿真出来的机器称为虚拟机(VM),它通过仿真软件使用主机系统的硬件资源,虚拟化后具有与计算机相同的组件。
有许多可能的解决方案可供选择,但我们推荐以下两种主要解决方案:
VMware Workstation
——由 VMware 提供的虚拟机仿真行业标准软件。免费版在功能上有部分限制,并且每次只能运行一个虚拟机。如下图所示:
图 3.1 – VMWare Workstation 主网页
Oracle VirtualBox
——由 Oracle 提供的虚拟机仿真软件,免费且易于用于一般用途(可通过www.virtualbox.org获取)。如下图所示:
图 3.2 – Oracle VirtualBox 主网页
VMware 的完整版,当然是最完整的解决方案,但 Oracle VirtualBox 在全球用户中也很有名。无论如何,我们的实验室设置需要虚拟化软件,因此这两者中的任何一个都可以。请务必检查配置部分并按照建议的步骤进行操作。
至于模拟移动设备和物联网设备,Android 操作系统可以很容易地进行仿真。稍后我们将看到如何正确设置仿真。
在概述了用于启用我们实验室设置的基础工具之后,我们现在介绍 Kali Linux,这是道德黑客最重要的工具之一。Kali Linux 是一个 Linux 发行版,我们将在我们的仿真环境中用于客户端部分。
Kali Linux
Kali Linux 是一个专门用于道德黑客、渗透测试和信息安全的 Debian Linux 发行版。Kali Linux 在信息安全专家中非常有名,因为它提供了大量的安全工具,这些工具可以用来执行安全测试和模拟网络攻击。这些工具的阵列非常受欢迎且完整,甚至被恶意攻击者使用。我们必须强调一点:作为道德黑客,测试只能在得到明确同意的目标上进行。这当然包括攻击者自己拥有并打算用于执行测试的可能目标。因此,最简单的配置是基于使用运行该特定版本 Linux 操作系统的虚拟机,这样它可以在与仿真目标相同的虚拟化环境中使用,无论这些目标是网页应用还是其他类型的目标。
Kali Linux 提供了超过 600 种计算机安全工具,从黑客攻击到网络监控工具,涵盖了安全专业人士的广泛使用场景。套件中还包括一些针对网页应用安全的重要工具,其中一些工具专门用于发现和利用 SQL 注入漏洞。接下来我们将介绍其中的一些工具,具体如下:
OWASP ZAP
:ZAP
代表Zed Attack Proxy
,该名称已解释了它的功能:这款软件用作代理,通过 Web 浏览器使用,从而可以研究 Web 应用程序的行为,进行 动态应用程序安全测试(DAST)。通过 Web 浏览器发送的流量可以通过 ZAP 发送,允许它分析请求和响应的通信,从而根据互动情况发现可能的漏洞,包括 SQL 注入。ZAP 还内置了一个扫描器,向目标网站发送预格式化的请求,并根据相应的响应,可以识别 Web 应用程序中的漏洞。OWASP ZAP 由 OWASP 提供,OWASP 支持全球的安全专家,推动安全社区的发展和全球合作,同时也提供一个共享且有效的 Web 应用程序安全测试方法论。OWASP ZAP 如下图所示:
图 3.3 – OWASP ZAP – Zed Attack Proxy – 网页
Burp Suite
:由 PortSwigger 开发的 Burp Suite 是一套 Web 应用程序安全工具,类似于 ZAP,但可能功能更为丰富。然而,一些最有用的模块仅通过其 Pro 版本提供。Burp Suite 的使用方式与 ZAP 相同,作为 Web 应用程序攻击的代理,服务于相同的目的。如果您尚未拥有 Burp Suite Pro,我们建议使用 OWASP ZAP 来进行本书中包含的测试。Burp Suite 如下图所示:
图 3.4 – Burp Suite 的主网页
sqlmap
:sqlmap 是最著名的自动化 SQL 注入软件。它是一个命令行工具,可以用来识别和利用 Web 应用程序中的可能 SQL 注入漏洞。其检测引擎非常强大,经过多年优化,已成为 Web 应用程序安全测试中最相关的工具之一。sqlmap 如下图所示:
图 3.5 – sqlmap 的网页
SQLninja
:SQLninja 是另一个有用且强大的工具,旨在利用针对运行 Microsoft SQL Server 数据库的 SQL 注入漏洞,最终可能通过更具体的选项完全接管后台,从而实现远程访问数据库服务器。SQLninja 如下图所示:
图 3.6 – SQLninja 的网页
我们工具集的最后一部分,将在下一节中描述,是我们测试的主要推动力,因为它充当我们模拟环境的服务器端。
OWASP BWA 项目概览
OWASP 维持着大量子项目,得到了世界各地许多人的共同努力,旨在提升全球安全专业人士的体验,并提高 Web 应用程序安全问题的意识。道德黑客遇到的一个问题是,如何找到无需承担后果即可进行攻击的目标,来测试他们的知识。过去,一些组织,如 Web 应用程序安全软件供应商,会将特定的 Web 应用程序放到网上,允许测试软件的能力,或者只是为了普及 Web 应用程序安全知识。部分应用程序已经被移除,或者逐渐变得越来越难以找到,可能是因为它们已被停止使用,或者是为了测试旧版本的应用程序安全软件而设计的。OWASP BWA
项目旨在为人们提供一些这些 Web 应用程序的集合,以便在 Web 应用程序安全测试期间作为测试目标使用。这些 Web 应用程序通过 Ubuntu Server 虚拟机进行分发,该虚拟机作为 Web 服务器托管其中包含的应用程序。
该项目于 2015 年停止更新,但仍可下载虚拟机镜像。提供的虚拟环境将作为我们目标 Web 应用程序的基础。在 目标–配置你的目标 Web 应用程序 章节中,我们将看到如何设置机器。
由于可用的应用程序数量相当庞大(准确来说有 37 个),我们将重点介绍那些我们认为最适合练习 SQL 注入的应用程序,包括手动和自动化的练习。
为了让大家了解受漏洞影响的 Web 应用程序选择中心是什么样的,我们添加了一个截图,展示访问活动虚拟机时会看到的选择菜单。完成目标机器配置后,你将能够看到它,所以请确保不要错过相应的章节(目标–配置你的目标 Web 应用程序)。请查看以下截图:
图 3.7 – OWASP BWA 应用程序选择中心
这里是我们在实践部分将要涵盖的主要 Web 应用程序列表:
Mutillidae II
:由 OWASP 提供的引导式训练测试 Web 应用程序。我们已经在第二章,操作 SQL – 利用 SQL 注入中看到过这个应用程序触发错误的示例。Mutillidae II 在以下截图中展示:
图 3.8 – Mutillidae II 首页
- 代码注入彩虹:另一个测试 Web 应用程序,提供引导式挑战以练习注入漏洞(包括 SQL 注入)。可以在下面的截图中看到:
图 3.9 – 代码注入彩虹 SQL 注入页面
Peruggia
:一个故意设计为有漏洞的应用,提供了用于 Web 应用安全测试的现实用例场景。如下图所示:
图 3.10 – Peruggia 首页
- 破损的 WordPress:一个使用 WordPress 2.0.0 创建的示例博客,以存在许多漏洞而著称。这是我们在第二章中看到的同一个 WordPress 博客,SQL 注入攻击的利用——窃取凭证,用来展示凭证盗取。破损的 WordPress 如下图所示:
图 3.11 – 破损的 WordPress 首页
OWASP Vicnum
:一个非常简单、基础的 Web 应用,展示了不应用安全措施可能带来的严重后果。我们在第二章中也看到了这一点,SQL 注入攻击的利用,但是我们会在实践部分再次回顾它。OWASP Vicnum 如下图所示:
图 3.12 – Vicnum 项目的主页
随时可以使用提供的应用进行测试,独立于本书提供的指南。实践成就完美,在受控环境中练习可以让你按照自己的节奏提高技能。
提供的方法和工具代表了我们手中的武器库。现在,是时候开始设置我们的测试环境,以便我们可以最终开始实践部分。
攻击者 – 配置你的客户端机器
在本节中,我们将展示我们对用于执行攻击性测试的机器配置的建议。请记住,其他选项也是可能的(稍后将描述),但我们的建议优先考虑可用性,并涵盖广泛的使用场景。
在本书中,我们将使用运行 Kali Linux
的客户端机器。Kali Linux 是免费的,可以在官方 Kali Linux 网站 (www.kali.org) 下载作为启动镜像使用。每个镜像还包括在 Live 环境中运行系统的选项,因此不需要安装。由于我们使用虚拟机实验室来管理客户端和服务器端,我们将在本指南中使用常规安装。
在可用的下载选项中,我们推荐 Kali Linux 64 位,以获得与更新软件的最佳兼容性;如果主机无法正确处理 64 位虚拟化系统,也可以使用 Kali Linux 32 位。Kali Linux Light 也可以使用,但大部分软件工具需要逐个安装。出于安全考虑,确保你从网站下载的文件的 SHA-256 哈希值与以下截图中显示的相同:
图 3.13 – Kali Linux 下载页面
一旦你获得了操作系统镜像,就可以开始设置虚拟化系统了。
即使 VMware 被广泛认为是 IT 专业人员的行业标准,Oracle VirtualBox 也可以用于设置测试实验室,且没有使用限制。这两款软件都是有效的,可以在没有明显差异的情况下使用。出于可用性考虑,我们将使用免费提供的 Oracle VirtualBox 进行测试,以下截图展示了该软件:
图 3.14 – Oracle VirtualBox 下载页面
一旦你获取了所选择的磁盘镜像和虚拟化软件,接下来可以创建一个运行 Kali Linux 的新虚拟机。
创建一个新的客户端虚拟机
在这里,我们提供了一个使用 VirtualBox 创建新虚拟机的分步指南。客户端和目标虚拟机的配置中会有许多共同的步骤,因此请确保按照我们的分步指南进行操作。按以下步骤进行:
-
点击适当的创建新虚拟机按钮,并根据你之前下载的 Kali Linux 版本(64 位或 32 位),在向导中完成设置,选择
Linux
作为操作系统,选择Other Linux (64-bit)
作为发行版(我们在此附上了创建向导的截图)。该过程在以下截图中展示:图 3.15 – 我们的 Kali 测试机器设置 – 文件路径和系统选择
-
在选择版本后,你将被提示配置机器的技术设置。根据你的主机计算机的性能,选择为虚拟机分配适当的随机存取内存(RAM)。我们推荐的最低要求是 1,024 兆字节(操作系统本身需要大约 512 兆字节才能正常运行)。该过程在以下截图中展示:
图 3.16 – 我们的 Kali 测试机器设置 – 内存设置
-
对于次级存储,你将被提示选择一个虚拟硬盘。我们推荐至少 8 GB 的磁盘大小,但由于这只是一个测试虚拟机,因此 8 GB 应该足够了(Kali Linux 安装操作系统本身就需要约 3 GB 的磁盘空间)。该过程在以下截图中展示:
图 3.17 – 我们的 Kali 测试机器设置 – 硬盘设置
-
在首次启动时,选择之前下载的 Kali Linux 镜像作为启动介质,开始在虚拟机上安装操作系统。根据语言、访问凭据和所选桌面环境选择适当的选项。操作系统安装完成后,你将可以自由使用虚拟的 Kali Linux 机器进行测试。
最后,我们建议安装我们之前提到的附加软件,位于Kali Linux子章节的理解实用方法并介绍主要工具部分。
首先,确保通过运行以下命令更新所有 Kali 应用程序包,并确保它们是最新版本(首先确保 Kali 机器已连接到互联网):
sudo apt-get update
sudo apt-get upgrade
要安装 OWASP-ZAP,请在终端中运行以下命令:
sudo apt-get install zaproxy
要安装 SQLninja,请在终端中运行以下命令:
sudo apt-get install sqlninja
sqlmap 应该已经包含在默认的 Kali 发行版中。然而,为了以防万一,请也运行以下命令:
sudo apt-get install sqlmap
Kali Linux 还提供许多其他安全工具,除了我们在本书实践部分中使用的那些工具。可以在受控和安全的环境中自由探索这些工具,而不会对任何第三方造成损害。
一旦客户端虚拟机设置完成,我们可以继续设置将在测试中使用的目标虚拟机。
目标 – 配置你的目标 Web 应用程序
在我们所有的测试中,我们将使用之前设置的 Kali Linux 安装作为客户端。为了覆盖本书中之前描述的情况(Web 应用程序和 IoT 设备),我们需要多种目标配置。
首先,我们将展示如何设置作为 Web 应用程序 SQL 注入攻击测试目标的虚拟机,使用 OWASP BWA 虚拟机中包含的 Web 应用程序。在这种情况下,设置非常简单直观。
通过点击 OWASP BWA 主页面中报告的 Sourceforge 链接,下载 OWASP BWA 虚拟机的最新版本:
sourceforge.net/projects/owaspbwa/files/
不幸的是,最近几个月,OWASP BWA 的主页已经迁移到另一个平台,因此它只能作为一个格式不规范的页面访问,等待迁移到新平台。你可以通过以下网址找到它:
owasp.org/www-project-broken-web-applications/migrated_content
以下截图展示了 OWASP BWA 的主页:
图 3.18 – Sourceforge.net 上的 OWASP BWA 下载页面
从 Sourceforge 页面选择下载最新版本以开始下载。由于文件大小(1.8 GB),这个下载可能需要一些时间,具体取决于你的互联网连接带宽。
下载的文件是一个压缩文件夹,包含虚拟机文件和硬盘镜像。现在,我们将看到配置服务器虚拟机是多么简单,因为它比客户端虚拟机要容易得多。
创建 OWASP BWA 虚拟机
按照我们在创建新客户端虚拟机部分中看到的相似方式进行操作,以下是步骤:
-
使用你的仿真软件创建一个新的虚拟机(Linux,64 位),并设置 1,024 MB 的内存。你可以参考图 3.15和图 3.16,因为选项将是相同的。
-
一旦系统提示你选择虚拟硬盘,选择一个现有的硬盘(在本例中,选择
OWASP BWA
文件夹中的OWASP Broken Web Apps-cl1.vmdk)。以下截图展示了该过程:图 3.19 – 选择现有磁盘以用于 OWASP BWA 虚拟机
-
由于虚拟机存在大量安全漏洞,因此将其设置为仅主机连接,使其仅在你的主机机器的本地 IP 地址上可见。进入 VirtualBox 菜单中的设置面板,并从网络选项卡配置虚拟机网络适配器。以下截图展示了该过程:
图 3.20 – a—VirtualBox 主屏幕中的“设置”选项
网络面板在设置菜单中的显示如下:
图 3.21 – b—设置菜单中的网络面板
-
一旦虚拟机准备好,你可以通过屏幕上显示的 IP 地址进行访问。通过浏览器访问该 IP 地址,你可以看到可用的应用程序正在运行,正如图 3.7所示的选择中心。在下图中,你可以看到服务器虚拟机启动后的屏幕,显示了用于通过 HTTP 连接 Web 应用程序的 IP 地址:
图 3.22 – OWASP BWA 的主屏幕,包含访问易受攻击的 Web 应用程序的说明
这就完成了 Web 应用程序环境的设置。在接下来的部分,我们将处理其他仿真设备。
目标 – 配置你的目标仿真设备
接下来我们将演示如何设置移动设备和物联网仿真设备。
在功能上,这些设备的操作范围更加有限:通常,物联网设备具有非常有限的计算能力,通常依赖于简单的 Web 服务,且没有任何丰富的图形设置。
来自 OWASP BWA 虚拟 Web 服务器的某些应用程序实际上可以模拟这种行为,通过提供 Web 服务器的应用程序编程接口(APIs)。我们可以说,我们已经为 Web 服务交互设置了一些环境。
附注:OWASP IoT 安全测试框架和 IoTGoat 项目
一个有趣的 IoT 安全测试方法再次来自 OWASP:IoT 安全测试框架今年发布,提供了一种彻底的方法论,用于评估和测试 IoT 环境中的漏洞,类似于 OWASP 提供的 Web 应用程序和移动应用程序测试框架。
与这个新框架相结合,OWASP 发布了另一个用于测试的实用工具,同时保持一切合法并在可控的环境中进行:OWASP IoTGoat 虚拟机。这个项目在某些方面呈现了 IoT 对应于 OWASP BWA 项目的角色,通过提供一个故意存在漏洞的设备模拟,包含许多 OWASP IoT 漏洞前十中的漏洞。我们认为无论如何都值得查看它,访问官方 GitHub 页面:github.com/OWASP/IoTGoat
。
在处理 Web 服务和 Web 应用程序后,这些也构成了与 IoT 世界交互的主要方式,我们将设置模拟移动设备的方式。为此,我们需要一种方式来模拟移动应用程序环境,包括客户端。
首先,我们推荐最著名的 Android 移动应用程序编辑器,它也是免费的:Android Studio。
Android Studio
是一个完整的 Android 移动应用程序开发环境。它还提供了一个方便的 Android 模拟器,可以在同一台用于编程的计算机上,从客户端的角度测试开发的应用程序。这是我们用于 SQL 注入测试的主要功能。
从官方网站下载 Android Studio(developer.android.com/studio
),并继续安装。以下截图展示了 Android Studio:
图 3.23 – Android Studio 官方下载页面
从这里开始,您将有很多选项可以选择。大多数选项都可以选择,根据个人喜好即可,但我们需要的最重要功能是 Android 模拟器。安装时确保选择了此选项。以下截图展示了该过程:
图 3.24 – 在安装 Android Studio 时选择 Android 虚拟设备作为选项
启动 Android Studio,完成首次启动时的安装。可以自由设置偏好设置,但标准安装应该足够。一切设置完成后,你就拥有了模拟移动应用客户端所需的工具。正如你可能知道的那样,移动应用通常由客户端应用程序与 Web 服务组成。因此,我们需要一种部署并模拟 Web 服务的方法。虽然有很多选择,但我们推荐使用 Eclipse,它是最受开发者欢迎的 Java 环境之一。我们将使用 Eclipse 来运行简单的 Java 代码,这些代码将用于模拟我们移动应用所需的 Web 服务。
在安装 Java 环境之前,为了使 Web 服务能够正常工作,我们还需要安装另外两项软件,具体如下:
-
Apache Tomcat
(可在tomcat.apache.org/download-90.cgi
下载) -
MySQL Community
(可在dev.mysql.com/downloads/installer/
下载)。在这种情况下,选择自定义安装,只安装MySQL Server
和MySQL Workbench
,如下图所示:
图 3.25 – MySQL 自定义安装
在安装完 Tomcat 和 MySQL 后,访问 MySQL 网站,然后进入 下载 部分,下载用于与 Tomcat 配合使用的 Java 连接器 (dev.mysql.com/downloads/connector/j/
)。确保选择 平台无关 作为操作系统。该过程如下面的截图所示:
图 3.26 – 下载 Java 连接器
下载后,将文件移动到 Apache Tomcat 安装文件夹中的 lib 文件夹(通常是 C:\Program Files\Apache Software Foundation\Tomcat 9.0\lib)。
最后,前往官方 Eclipse 下载页面 (www.eclipse.org/downloads/
) 并按照说明进行操作。以下截图展示了该过程:
图 3.27 – 官方 Eclipse 下载页面
在某些情况下,安装程序可能找不到你计算机上安装的 Java 虚拟机(JVM) 。如果是这种情况,它会弹出一个窗口,要求你浏览丢失的文件。选择从弹出窗口中浏览该文件的选项,并在计算机上浏览,指向有效的 javaw.exe
文件。你应该能够在 Android Studio 安装文件夹中的 jre\bin
文件夹中找到该文件。
在安装过程中,选择 Eclipse IDE for Enterprise Java Developer
选项,这对我们来说非常有用,因为它包含了部署 Web 服务所需的所有工具。如下图所示:
图 3.28 – Eclipse 安装期间选择突出显示的选项
此时,安装应该开始了。请继续根据路径和可访问性设置您的偏好。
操作实验室
现在实验室的组件已经完全设置好,我们准备开始让它工作。
设置 OWASP BWA 实验室
以下是我们建议的设置 OWASP BWA 实验室部分的步骤:
-
首先,从 VirtualBox 运行 OWASP BWA 虚拟机。启动后,它应该显示出图 3.21中所示的屏幕。现在,计算机上可以通过屏幕上显示的地址访问完整的 Web 应用程序列表。
-
要使用 Kali Linux,请从 VirtualBox 运行您的 Kali 虚拟机。请记住,只要 OWASP BWA 在当前设置下运行,它也可以访问 BWA 中可用的 Web 应用程序。以下截图展示了这一点:
图 3.29 – 从 Kali 虚拟机看到的 OWASP BWA 选择中心
这样,您计算机上的任何模拟设备都可以连接到 OWASP BWA 服务器,从而使得可以测试各种攻击范围。显然,也可以在不使用任何虚拟机的情况下,从您自己的计算机舒适地执行手动攻击。我们将主要使用 Kali Linux 进行高级或自动化攻击。
至于 Android Studio 和 Eclipse,我们将在下一章中更详细地处理这些工具,因为我们将在某些特定用例中使用它们。无论如何,为了使 Android 模拟器正常工作,我们需要首先设置一个虚拟设备。
设置 Android 虚拟设备
在这里,我们正在使用 Android Studio 设置一个Android 虚拟设备(AVD)。请按照我们提供的简单逐步指南操作:
-
从 Android Studio 的主屏幕选择AVD 管理器选项。它应该位于屏幕的右上方,如下图所示:
图 3.30 – Android Studio 中的 AVD 管理器
-
进入AVD 管理器后,选择创建虚拟设备…。然后选择模拟模型(通常默认选择的是 Google Pixel 2:您可以选择它)。该过程在以下截图中展示:
图 3.31 – AVD 管理器的主屏幕
-
一旦选择好,您需要选择一个系统映像。从推荐的系统映像中选择
Android 10
。然后,所选映像将被下载。根据您的互联网连接速度,这可能需要相当长的时间。该过程在以下截图中有所展示:图 3.32 - 新设备的系统映像选择
-
下载完成后,AVD 管理器的主屏幕将列出你新配置的模拟设备。
模拟设备的工作方式与虚拟机完全相同。它将复制 Android 移动设备的功能。此功能专门用于测试开发的 Android 应用程序的运行时。我们将在本书的下一章看到一些示例。
总结一下:你可以在计算机上运行所有目标并进行所有测试,而无需物理机器或额外的设备。然而,如果你的机器受到主内存等限制,我们建议分别尝试每个场景,以免一次性使计算机的虚拟化过多而过载。然而,你可以根据需求调整虚拟机的系统设置。
总结
在这一章中,我们设置了执行测试所需的实验室环境。以下是我们到目前为止设置的简要清单,并回顾了我们将使用的工具。
我们在道德设置中执行测试的主要工具是使用虚拟化软件,借此在不损害任何第三方的情况下测试我们的攻击技术,同时使用免费工具和软件。
除了可能是我们自己的计算机外,我们执行基于 Web 的攻击的主要客户端将是 Kali Linux 虚拟机,用于高级和自动化攻击技术。为了模拟一个易受攻击的目标 Web 服务器,我们将使用 OWASP BWA 虚拟机,其中包含易受 SQL 注入攻击的传统 Web 应用程序,以及 Web 服务攻击(表征状态转移(REST))场景,其他应用程序模型,如物联网架构,通常依赖于这些场景。我们的移动应用程序场景将通过 Android Studio 运行,使用其内置的设备模拟器作为客户端,以及运行在我们计算机上的 Web 服务。
所有这些不同的场景可以单独在我们的计算机上运行,而不会过度占用我们的资源。无论如何,设置可以根据你的限制和需求进行自定义。
下一章将是实际部分的核心,重点是按照逐步方法运行我们在第二章中看到的攻击技术,操纵 SQL – 利用 SQL 注入(以及更多)。这些操作将在我们本章所看到的相同虚拟环境中执行。
我们希望你在下一章中能玩得开心,因为你将亲自体验到我们迄今为止所看到的内容。
问题
-
什么是虚拟化软件?为什么我们在实际部分中需要它?
-
什么是 Kali Linux?我们为什么在实验室环境中使用它?
-
什么是 OWASP BWA 项目?为什么我们在测试中需要它?
-
我们正在处理什么样的模拟设备?
-
在未经第三方同意的情况下对 SQL 注入进行测试是否合法?
第四章:攻击 Web、移动和物联网应用程序
现在我们进入了旅程的有趣部分——本书实践部分的核心。到目前为止,我们已经了解了 SQL 注入的基础和原理,包括成功的 SQL 注入攻击可能带来的效果。我们还提供了一个安全受控的环境,任何人都可以按自己的节奏体验 SQL 注入攻击的组成部分。
本章将讨论针对传统 Web 应用程序的 SQL 注入攻击,这是最常见的攻击场景,使用手动和自动化技术,依赖我们在上一章中讨论的工具集。
本章分为以下几个部分:
-
攻击传统 Web 应用程序 - 手动技术:本节展示了针对 OWASP
Broken Web Applications** (
BWA**) 虚拟 Web 服务器中的易受攻击 Web 应用程序手动执行的 SQL 注入攻击。你已经在第二章中遇到过类似的内容,SQL 注入操作 - 利用 SQL 注入。不过,在这里,我们将通过引导你逐步了解攻击者会如何进行,来尝试一种更现实的方法。 -
攻击传统 Web 应用程序 - 自动化技术:我们的目标将依然是包含在 OWASP BWA 项目中的 Web 应用程序。不过这一次,我们将展示自动化 SQL 注入工具的能力,这些工具被攻击者(以及安全专业人员)用于提高效率。
-
攻击移动目标:本节将探讨移动应用程序如何也可能容易受到 SQL 注入攻击,并展示实际的示例。
-
攻击物联网目标:无论在何种上下文中,SQL 数据库都可能遭受 SQL 注入攻击。物联网世界也不例外。我们在这里展示一个可能对物联网系统感兴趣的攻击场景。
技术要求
对于这一非常实用的章节,我们强烈建议你熟悉主要的相关工具。我们推荐以下资源,包括上一章的参考资料:
-
https://www.virtualbox.org/
请查看以下视频,观看代码实践:bit.ly/32d3s2b
攻击传统的 Web 应用程序–手动技术
让我们从对 OWASP BWA Web 应用程序的手动攻击开始。在第二章中,我们已经发现了一个通过 SQL 注入提取信息的简单攻击点——操纵 SQL – 利用 SQL 注入,但我们将假设每个应用程序都是独立的,并且没有共享 MySQL 实例。因此,我们不会考虑 OWASP Vicnum 应用程序,因为这会让我们太轻松。每个应用程序都将被视为独立的目标,这样我们就可以探索其中的内在漏洞。在本节中,我们将对 OWASP BWA 的三个应用程序进行 SQL 攻击:Mutillidae II
、Magical Code Injection Rainbow
和 Peruggia
,并在引导环境中实践到目前为止所学的内容。
攻击 Mutillidae II
我们的第一个目标有点像热身——Mutillidae II
是一个设计用于提供 SQL 注入测试环境的应用程序,采用教育性方法,同时提供一些有关可能执行的攻击的提示。您可以通过左侧的下拉菜单进入 SQL 注入部分 (OWASP 2013** |
A1 - 注入(SQL)**):
图 4.1 – Mutillidae II 中的 SQL 数据提取页面
现在让我们展示如何攻击这个 Web 应用程序中的 SQL 注入漏洞表单。
通过 SQL 注入提取数据
让我们通过下拉菜单进入 Mutillidae II 提供的 SQL 注入测试数据提取页面。首先,我们将执行 SQL 注入攻击的第一步:通过在 Name
字段插入最基本的注入字符——单引号,检查是否存在输入验证:
图 4.2 – Mutillidae II 中的 SQL 数据提取页面:Web 表单
仅插入 SQL 注入启用字符就足以证明 SQL 注入漏洞的存在。如果出现 SQL 错误,意味着输入被解释,导致查询语法错误。插入单引号后,我们确实得到了 SQL 语法错误:
图 4.3 – Mutillidae II 提供的错误信息可视化(0 安全性)
在 Mutillidae II 设置为最低安全级别的情况下,错误信息是完整的,还帮助我们可视化完整的错误信息。让我们通过点击 Toggle Security
选项一次并应用客户端安全性来提高安全级别:
图 4.4 – Mutillidae II 中的客户端控制示例
在这种情况下,应用了客户端控制,防止了空输入字段并阻止了可疑字符,前提是两个字段都已填写,例如将' -- -
作为用户名,并将任何字符作为密码(如果没有应用安全控制,则该输入应完全忽略单引号字符后的内容)。
然而,客户端控制不足以防止 SQL 注入。假设我们通过使用 URL 地址中的参数来绕过它们怎么办?(是的,登录是通过GET
请求进行的,通过将数据作为输入参数发送到 URL 中。)
让我们以以下方式修改页面 URL。在这里,我们有一个正常的页面 URL,用户名为a
,密码为b
:密码:
我们只是在 用户名:处添加了一个单引号
这会触发另一个错误信息,如下所示:
图 4.5 – Mutillidae II 提供的错误信息可视化(客户端安全性)
请记住,如果这是POST
请求而不是GET
,仅修改请求字段就足以引发 SQL 注入。这突显了服务器端安全控制的重要性。这是确保输入被正确处理的唯一方法,无论注入方式如何。
因此,我们证明了该页面易受 SQL 注入攻击。输入被解释为 SQL 语法,由于语句不正确导致语法错误。错误信息还泄露了一些关于查询结构的重要信息,这些信息可以被用来对我们有利,且揭示了一个名为accounts
的表,其中有username
和password
字段。
让我们进一步进行数据提取,好吗?既然我们已经看到 SQL 注入是可能的,我们可以尝试看看其他技术是否有效,从而提取数据。在一种贪婪的方式下,我们现在尝试查看同义反复(tautologies)是否有效。
让我们尝试在用户名字段中输入臭名昭著的 ' or 1=1 -- -
字符串(如果是客户端安全问题,我们需要像之前的例子那样编辑参数,而不是实际使用表单)。这个尝试会导致以下截图中的结果:
图 4.6 – 所有账户信息记录的结果页面
使用这个同义命题,我们已经找到了所有应用程序用户的完整登录信息。此时,攻击者可以获得管理员(admin)权限,可能会导致严重后果。
所以,应用程序很可能对提取信息的 SQL 注入攻击存在漏洞。我们假设这个攻击已被 Web 应用程序阻止,并尝试一种更微妙的方法。
我们现在将尝试使用 UNION
查询提取数据库版本信息。我们需要通过反复试验的方法来查看应用程序显示了多少列,以及哪些列被显示。我们为您节省了这个过程:插入 ' UNION SELECT 1,@@VERSION,3,4,5,6,7 -- -
将会有效,因为显然,accounts
表(我们通过错误信息知道了它的名称)有 7 列,而应用程序只显示了列 2、3 和 4:
图 4.7 – 使用 UNION 查询显示系统版本的结果页面
此时,我们不仅发现了系统的版本信息,还发现了 accounts
表中列的总数,因为原始查询为该表选择了所有字段(SELECT
*)。
我们现在应该能够提取数据库架构的信息。我们已经通过错误信息和版本信息知道数据库是 MySQL。所以,让我们按照 第二章中讨论的内容,要求获取数据库架构中的架构名称,SQL 注入攻击 – 利用 SQL 注入。使用和之前相同的技巧,通过在第二个字段中显示我们需要的信息,我们可以提取数据库中包含的架构名称:
图 4.8 – 架构名称提取
我们现在可以继续进行 Mutillidae II SQL 注入的下一部分。
绕过身份验证的 SQL 注入
本部分内容关于绕过 Mutillidae II Web 应用程序的登录屏幕。您可以通过与上一部分相同的下拉菜单访问此部分:
图 4.9 – Mutillidae II 中的 SQL 数据提取页面
我们现在将面对一个典型的登录网页表单,需要用用户名和密码进行身份验证。首先,让我们通过触发一些错误来检查这个表单是否容易受到 SQL 注入攻击,方法是使用 SQL 字符,如单引号:
图 4.10 – Mutillidae II 登录页面显示未公开的错误
这一次,我们可以判断发生了错误。然而,应用程序没有显示(根据已知的最佳实践)完整的错误信息。尽管如此,我们知道表单存在漏洞,因为如果使用错误的账户信息,响应会有所不同。
我们已经从上一节提取了登录信息,因此我们将尝试使用已知的凭据进行admin
访问:
图 4.11 – 管理员访问成功
我们还可以检查该表单是否容易受到伪命题攻击,从而让我们获得访问权限。让我们尝试在用户名字段中插入' OR 1=1 -- -
字符串:
图 4.12 – 伪命题登录尝试
这个身份验证绕过攻击也会成功,重新授予我们管理员admin
账户的访问权限。请记住,即使我们能作为系统中的任何账户进行身份验证,攻击者也会尽力获取尽可能高的权限(毕竟,大多数应用程序允许创建用户级账户)。
现在让我们继续探讨这个应用程序的最后一种 SQL 注入形式。
INSERT 语句中的 SQL 注入
到目前为止,我们仅在SELECT
语句中研究了 SQL 注入。Mutillidae II 提供了一个账户创建页面,该页面与一个INSERT
语句相关联,用于将新记录添加到数据库的账户表中。它还提供了另外两个可以向数据库添加数据的页面。然而,我们在本节中仅讨论这个页面,以免占用其他话题的篇幅。欢迎你自己探索其他两个页面:
图 4.13 – 访问账户创建页面
账户创建页面不应该返回任何记录,因为其主要目的是(没错,你猜对了)将用户添加到数据库的账户表中。因此,在这个上下文中,它看起来是一个尝试盲注 SQL 注入的好地方:
图 4.14 – 账户创建表单
首先,我们可以触发错误信息,检查查询语法,并检查我们可以在哪里注入命令:
图 4.15 – 插入单引号作为用户名的错误信息
现在我们已经有了查询结构,这样我们就可以通过 SQL 语法来修改命令。一个例子可能是通过使用 SELECT
语句的子查询来检索敏感信息。让我们尝试使用 MySQL 根账户密码作为签名创建一个用户。我们将首先尝试在用户名字段中使用 test','test',(SELECT password FROM mysql.user WHERE user='root'))-- -
载荷,以获得以下结果:
图 4.16 – MySQL 错误;子查询返回多个结果
在这里,SELECT
查询显然返回的行数比我们预期的更多。为了解决这个问题,我们需要一种方法来查看单个结果。一种简单的解决方案是在 SELECT
查询的末尾使用 LIMIT 1
子句,它用于将结果限制为仅一个,从而实现成功。让我们使用登录面板,最终通过我们新创建的凭据来检查我们的值:
图 4.17 – 通过成功身份验证后的 Mutillidae II 上方面板
在这里,攻击者可以使用注册表单来检索敏感信息(在这种情况下,是 MySQL 根账户的密码哈希)。即使它不是一个设计用来返回数据的查询,这也能实现。这个例子是为了展示 SQL 注入如何为攻击者提供多种工具,只要攻击者知道合适的 SQL 语法。
现在我们可以进入我们的速成课程的下一个 Web 应用程序:Magical Code Injection Rainbow。
Magical Code Injection Rainbow
在 Magical Code Injection Rainbow
中,我们有一个用于代码注入训练的应用程序。我们关注的是 SQL 部分,恰当地命名为 SQLol
,它还提供了一些包括服务器端防御的示例。这一次,我们将通过这个应用程序提供的前六个挑战,并为每个挑战提供解决方案。随时可以自己尝试这些(或接下来的挑战)。此时,在 MySQL 文档的帮助下,您应该能够利用在 第一章 中学到的知识,完成它们,SQL 注入的结构化查询语言,以及 第二章,操控 SQL – 利用 SQL 注入:
图 4.18 – Magical Code Injection Rainbow SQLol - 挑战界面
通过从主屏幕选择 SQLol
,然后选择 Challenges
来访问这些挑战。你将始终有一个文本字段用于插入载荷,因此你知道那是插入 SQL 载荷的唯一方式。
挑战 0 – 你好,世界!
这是列表中最基本的挑战。在这里,您需要使用 SELECT
查询从数据库的用户表返回所有用户名,并在 WHERE
子句中执行注入。这是我们迄今为止看到的 SELECT
查询的典型设置。
这很容易做到。只需提供一个总是为真的条件,并且我们知道如何进行类推。
我们的有效负载将是 ' OR 1=1 -- -
,导致我们期望的输出:
Figure 4.19 – SQLol 挑战 0 结果
这很简单,对吧?我们只是以可能的最简单的 SQL 注入方式返回了所有用户名。现在让我们继续进行第二个挑战。
挑战 1 – SQL 注入 101
这也是一个相当简单的挑战;话虽如此,它需要一些侦察,以便发现查询结构。这一次,我们需要找到包含数据库中存在的社会安全号码的表,并在查询输出中返回完整内容。再次,我们使用 SELECT
查询,并在 WHERE
子句中进行注入:
-
首先,我们需要找出社会安全号码表的名称。为此,我们需要使用
UNION
查询检查 MySQL 的information_schema
表。我们首先需要使用' UNION SELECT table_name FROM information_schema.tables -- -
有效负载找到表名。或者,我们还可以通过添加诸如LIKE
等带有特定期望特征的子句(例如,对于包含ssn
的表名,使用 WHERE table_name LIKE '%ssn%')来细化搜索:Figure 4.20 – SQLol 挑战 1 结果
-
此时,我们需要使用
' UNION SELECT column_name FROM information_schema.columns WHERE table_name='ssn' -- -
有效负载从表中提取列名:Figure 4.21 – SQLol 挑战 1 结果
-
现在我们只需逐个字段查询
ssn
表。或者,我们可以使用更加优雅的解决方案,即CONCAT()
运算符:
Figure 4.22 – SQLol 挑战 1 最终结果
在这里,我们有所有用户的社会安全号码集中在一个地方。让我们继续进行第三个挑战。
挑战 2 – 单引号过滤的失败
这个挑战与前一个挑战几乎一模一样,但有一个小变化:单引号字符,这是 SQL 中最常见的激活器,完全从查询字符串中忽略,使我们刚刚执行的攻击失效。然而,净化措施只是在输入级别忽略了字符。但是,如果它在运行时被数据库评估怎么办?考虑以下步骤:
-
我们使用
CHAR(27) UNION SELECT 1 -- -
负载将1
的值添加到查询末尾。CHAR()
是 MySQL 中支持的一个函数,用于将数字转换为其 ASCII 字符等效值。在这种情况下,它是单引号:图 4.23 – SQLol 挑战 2:击败单引号转义
-
在这一点上,我们可以通过用
CHAR(27)
替代单引号来执行挑战 1 攻击:
图 4.24 – SQLol 挑战 2 最终结果
现在让我们继续下一个挑战。
挑战 3 – 死囚
这个挑战是第一个挑战的另一个克隆版本。不同之处在于,这次它每次只会显示一个结果。
幸运的是,我们已经知道表的内容,因此可以跳到攻击的最后一步,直接进入此特定挑战所需的方法。
我们可以尝试相同的负载。然而,这次,在末尾添加LIMIT 1
,确保我们的 SQL 查询仅返回一个结果,无论应用服务器为此挑战所采取的措施如何,然后使用OFFSET
逐个查看每个结果。因此,返回第二个结果的负载将是' UNION SELECT CONCAT(name, " ", ssn) FROM ssn LIMIT 1 OFFSET 1-- -
,因为OFFSET
从0
开始表示第一个结果,并且对于每一行后续结果递增:
图 4.25 – SQLol 挑战 3 的第二个结果
现在我们可以继续下一个挑战。
挑战 4 – 错误战争
这个挑战是社会保障号挑战的另一个克隆版本。不过,这次输出不会以可视化方式呈现。这个挑战的目的是仅通过提供的详细错误信息提取信息,而不使用盲目 SQL 注入技术。
我们最好的方法是将查询结果显示在错误消息中。实现这一目标的一种可能方式是使用某种非严格 SQL 语法的表达式。
其中一个例子是ExtractValue()
函数,它从XML
(第一个参数)中提取值,使用XPATH
语法(第二个参数)。我们需要确保其中没有空格,以免产生 SQL 语法错误。相反,我们需要通过错误构造的XPATH
语法引发 XML 评估错误,并将我们的 SQL 查询插入其中。这样可以确保 SQL 语法正确评估,查询结果会在错误消息中泄露。
我们将尝试以下负载。请注意,由于每次只能查看一个结果,我们需要在每次更改偏移量时使用LIMIT 1
子句:
'AND ExtractValue('randomxml',CONCAT('=',(SELECT CONCAT(name,'-',ssn) FROM ssn LIMIT 1 OFFSET 0)))='x
有效载荷按预期工作,查询结果显示为 XPATH
语法错误。我们只需迭代查询与所有剩余的偏移量,并且可以通过错误消息提取整个表的内容:
图 4.26 – SQLol 挑战 4 结果
现在我们已经展示了这种有趣的攻击方法,最后让我们进入这个应用程序中的最后一个挑战。
挑战 5 – 盲目运气
这个最终挑战是另一个社会保障号码挑战。然而,这次输出和错误信息没有可视化。挑战的内容是使用盲目 SQL 注入技术提取信息。为了帮助我们,我们有布尔结果来告诉我们查询是否成功(即,是否返回至少一条记录)。
如果我们要从 SSN 表中提取信息,那么这次我们需要使用推理技术。幸运的是,我们不需要检查响应中的特别难解的线索,因为我们可以依赖布尔结果。首先,为了显示布尔结果,我们将通过查询 information_schema.tables
表来查找 ssn
表。我们将注入 ' UNION SELECT table_name FROM information_schema.tables WHERE table_name='ssn'-- -
有效载荷来检查它:
图 4.27 – SQLol 挑战 5 布尔结果
现在我们知道了什么是真正的结果,我们可以通过检查来猜测输出。由于我们需要重建数据,我们需要一种仅通过布尔答案来识别表内容的方法。我们可以使用的最常见技术是通过 SUBSTRING(s, d, n)
函数检查单个字符。该函数接受字符串 s
,该字符串的 n
个字符,位置 p
。SUBSTRING('hello', 1, 1)
将返回 h
,它位于第一个位置(1)后面 1
个字符的位置。
对于每条记录,我们将逐个检查字段的字符,使用 SUBSTRING()
函数和 LIMIT 1 OFFSET
子句,因为我们需要单独检查每条记录,以便确定它们。这个过程确实很长,但我们还有一个加速整个过程的小技巧:二分查找。我们将使用 ASCII()
函数,该函数返回单个字符的 ASCII 编码,并且每次与一个数字进行比较,这个数字将作为二分查找的枢轴。在 ASCII 编码中,我们有 255 个可能的值,所以我们的最佳枢轴值将是中间值(128)。通过与 128 进行比较,我们可以知道该字符属于范围的下半部分还是上半部分,并且每次将可能的范围分为两部分。
例如,如果我们的检查结果显示字符的 ASCII 值大于或等于 128,那么下次我们将尝试以 192 作为支点。如果不是,我们将降低到 64。每次,我们都会将范围分成两部分,从而大大减少猜测的步数。
在本次演练中,我们将直接跳到 ssn
表。然而,对于完整的攻击,你应该将这种方法应用于所有先前的发现步骤和表字段。我们更倾向于让读者自己决定这一点。
让我们尝试使用 ' OR ASCII(SUBSTRING((SELECT NAME FROM SSN LIMIT 1 OFFSET 0),1,1)) >= 128 -- -
载荷,将这一原则应用到我们特定的案例中,使用 128 作为我们搜索的支点:
图 4.28 – SQLol 挑战 5 推断尝试;无结果
以下是二分查找的逐步迭代过程:
-
通过错误结果,我们可以推断出我们的字符属于前 128 个(0 到 127)ASCII 字符。
-
使用 64(128/2)作为二分查找的支点返回了正确的结果。这意味着我们的字符在 64 和 127 之间。
-
以 96(64 + 32)为支点没有返回任何结果,所以我们将尝试 80(96-16)。仍然没有结果。
-
我们的下一个尝试是 72(80 - 8),成功了。这意味着接下来我们将尝试 76(72 + 4)。
-
76 失败,这意味着我们的数字在 72 和 75 之间。此时,我们将尝试 72:
图 4.29 – 推断的最后一步;直接比较(成功)
根据这些步骤,我们知道第一条记录的第一个字符对应的 ASCII 字符是 72,即大写 H
(Herp Derper
表中的名字首字母)。
最后,通过对所有条目和字段的所有字符应用相同的原则,我们可以从表中获取所有值,并进行双重检查:
图 4.30 – SQLol 挑战 5;推断复核
这就结束了我们通过《魔法代码注入彩虹》中的 SQL 挑战的指导性介绍。我们很愿意继续这个有趣的演练,但不想给这个应用程序占用太多篇幅,因此接下来我们将聚焦于更广泛的目标。现在,我们将继续本节的最后一个 OWASP BWA 目标:Peruggia 网络应用。
攻击 Peruggia
如 第三章 《设置环境》中所述,Peruggia 是一个故意设置为易受攻击的 Web 应用程序,它模拟了一个常规的(尽管可能过时的)Web 应用程序的行为。 在这种情况下,我们不会有教程或挑战,只有我们和应用程序,完全没有提示或帮助:
图 4.31 – Peruggia 主界面
现在我们来看一下如何利用 SQL 注入攻击这个应用程序。
SQL 注入在登录面板中的应用
我们的第一站是应用程序的登录面板。我们可以通过点击页面顶部的登录来访问它。在这里,我们遇到了一个情况,即我们的 SQL 注入尝试没有任何输出。事实上,甚至没有显示 SQL 错误(毕竟,这应该是在安全应用程序中的表现)。
此外,通过检查页面及其响应和交互,我们无法从成功的 SQL 注入尝试与失败的 SQL 注入尝试中区分出任何有意义的差异。这意味着我们无法从登录表单中提取信息。该应用程序通过不允许数据库服务器与用户直接通信,在这种情况下执行了一个好的设计原则。然而,这还不够。即使应用程序不让数据库服务器暴露查询结果、错误或任何其他有意义的信息,这并不意味着它不容易受到 SQL 注入攻击。
让我们尝试最简单的 SQL 注入攻击:同义命题攻击。在大多数情况下,像这样的攻击会导致完全绕过登录,从而使我们能够访问该应用程序。在以下截图中,我们正在尝试如前所示的同义命题攻击:
图 4.32 – Peruggia 登录绕过尝试
同义命题攻击已成功。这不仅意味着该 Web 应用程序容易受到 SQL 注入攻击,而且我们还可以利用登录界面进行推理攻击。每当成功登录时,这意味着我们执行的布尔检查为真,如下图所示:
图 4.33 – 成功管理员登录后的 Peruggia 上层界面
这意味着,类似于《神奇代码注入彩虹 SQLol》挑战 5,我们可以从数据库中提取我们想要的任何信息。让我们尝试在挑战 5 结束时做的双重检查查询。这次,我们将从不同的模式中提取数据,因此我们需要指定我们要提取数据的表的模式:
' OR (SELECT NAME FROM SQLOL.SSN LIMIT 1 OFFSET 0) = "Herp Derper" -- -
这将允许我们访问,这意味着我们检查的信息(再次)为真。通过这种方式,我们可以对数据库的内容应用推理技术。
这个例子确认了 SQL 注入作为一种灵活手段,从数据库中获取各种信息的强大威力。即使我们合法创建了一个新账户,这次尝试也可能成功。在 Peruggia 中,我们也有用户账户,登录绕过功能允许我们为每个用户登录,只要我们指定它:
图 4.34 – Peruggia 登录绕过示例
结果查询仅检查 WHERE
条件。在这种情况下,它只是检查是否存在一个用户名为 User
的记录,因为我们将查询的其余部分作为注释处理。如果出现此 SQL 注入示例,即使结果没有直接显示,盲注和推断的 SQL 注入原理仍然适用。
添加评论页面中的 SQL 注入
除了登录页面,我们还可以尝试在应用程序中测试其他参数。在 Peruggia 的主页上,我们可以通过标有 Comment on this picture
的链接,尝试访问(未认证)评论区:
图 4.35 – Peruggia 的添加评论页面(带 URL)
我们在页面 URL 中有一个 pic_id
参数,我们可以尝试对其进行操作。如果我们将其更改为一个不存在的 ID,例如 123456789
,我们将看到一个空图片:
图 4.36 – Peruggia 的不存在图片的添加评论页面
现在,我们将尝试插入正确的 SQL 语法,看看它是否会被应用程序评估。让我们尝试将 123456789 OR 1=1
插入地址栏作为 pic_id
参数。我们可以看到,即使我们插入了错误的 ID,我们依然能够看到 ID 为 1
的图片。这证明该参数会评估 SQL 输入,因此容易受到 SQL 注入攻击:
图 4.37 – pic_id 参数易受 SQL 注入攻击
此时,使用试错法,我们可以查看是否能够从数据库中可视化信息。我们可以尝试 UNION
查询技术,并猜测底层 SQL 查询中的参数个数。我们可以假设图片 ID 是这些参数之一,并且可能还有图片 URL(如果是不存在的 ID,我们会看到一个损坏的图片图标)以及页面上显示的 Uploaded By
。然而,使用 123456789 UNION SELECT 1,2,3
载荷仍然返回空图片,可能是由于 MySQL 错误。让我们尝试再使用一个参数,使用 123456789 UNION SELECT 1,2,3,4
载荷:
图 4.38 – 成功的 UNION SQL 注入
此时,我们知道底层查询有四个参数,第四个参数对应页面上的上传者(Uploaded By)值。我们可以使用此查询提取所有我们想要的信息。举个例子,我们将尝试提取应用程序的管理员账户密码。我们可以查询information_schema
表,提取应用程序(Peruggia)的架构及其表格,以找到与用户信息(users)对应的表格。然后,我们将使用123456789 UNION SELECT 1,2,username,password FROM users WHERE username='admin'
负载来返回与页面上上传者(Uploaded By)旁边的管理员账户密码:
图 4.39 – 在添加评论页面返回的管理员密码哈希
当然,我们可以使用相同的字段返回到目前为止看到的任何信息,例如root
MySQL 账户的密码、数据库系统版本,或属于其他表格的任何数据。本教程主要是为了展示手动 SQL 注入攻击的后果和影响(同时当然也能在安全和可控的环境中尝试这些攻击,获得一些乐趣)。
我们现在将进入实践部分的第二部分,展示使用 Kali Linux 中的高级和自动化工具能做些什么。
攻击传统 Web 应用程序 – 自动化技术
正如我们之前提到的,除了执行手动攻击技巧来利用 SQL 注入外,还可以使用一些特定的软件来处理 SQL 注入攻击中的部分任务,及时产生有用的结果。这些工具被攻击者和安全专业人员同时使用,因为它们优化了操作,并通过简化我们需要执行的任务帮助节省大量时间。
首先,我们将介绍使用Zed 攻击代理(ZAP)可以做什么,ZAP 是 OWASP 的攻击代理。
OWASP ZAP 用于 SQL 注入
OWASP ZAP 是一款多功能工具,包括一个攻击代理—一种用于拦截流量的软件下载工具,可以监控或修改流量后再发送到应用程序—并具有其他帮助自动化过程的功能。从这个角度来说,通过自动化,这个工具可以用于扫描 Web 应用程序的漏洞,测试收到的响应与特定输入的匹配情况。这个扫描功能可以用来识别多种漏洞类型,包括 SQL 注入。接下来让我们看看它的实际操作,具体如下:
-
首先,我们通过在 Kali Linux 机器的命令行输入
zaproxy
来启动软件。这应该会加载我们的图形界面,允许我们在右侧的面板(快速启动)中输入目标网站。我们将选择自动扫描模式,以便测试此工具的自动化功能:图 4.40 – OWASP ZAP 主界面
-
在选择 自动扫描 选项后,系统提示我们插入目标 URL 以开始测试。我们将插入 Peruggia Web 应用程序的 URL,这是一个足够简单的实例,能够展示 ZAP 的功能:
图 4.41 – OWASP ZAP 自动扫描面板
-
点击 攻击 后,我们的自动扫描将开始。首先,OWASP ZAP 会对应用程序进行蜘蛛扫描,探索应用程序的链接并检查可以探索的页面,所需时间非常短。自动分析的第二步是激活扫描器模块,它通过向应用程序发送特定的数据来检查漏洞,这些数据对应输入。几秒钟后,我们将在 警报 标签上看到我们的结果,如下所示:
图 4.42 – OWASP ZAP 警报标签显示找到的问题
OWASP ZAP 在几秒钟内就识别出了 用户名 参数和 pic_id
参数中的 SQL 注入漏洞(就像我们之前手动操作一样)。当然,大多数时候,这些自动化扫描器的结果需要手动验证,因为扫描器是根据接收到的响应来指示可能存在 SQL 注入漏洞的。尽管存在一定的不确定性,这个功能仍能在几秒钟内返回类似的漏洞提示,以及其他漏洞(如 跨站脚本 和 路径遍历)。
OWASP ZAP 的自动化功能也可以在你自己的浏览器中使用。为此,你需要将浏览器的代理设置为 ZAP 的代理(默认是 localhost
,端口是 8080)。或者,你可以直接从 ZAP 的界面启动一个浏览器实例,使用主界面上的 手动探索 选项。通过这种方式,OWASP ZAP 会在指定的 URL 上打开一个浏览器窗口:
图 4.43 – OWASP ZAP 手动探索面板
通过这种方式,一旦发现新页面,你可以独立地对每个通过手动探索识别的请求运行 ZAP 的 蜘蛛 和 扫描器 模块。在你浏览网站时,站点 标签会随着你访问的页面而更新,显示发送的不同请求:
图 4.44 – ZAP 提供的请求分析选项(攻击)
除了我们刚才描述的两个模块外,另一个非常相关的模块是 Fuzzer 模块,你可以通过选择Fuzz…选项来选择它。通过在请求中选择插入点,Fuzzer 模块可以尝试一组输入范围,检查是否有异常响应。要使用 Fuzzer 模块,你只需要选择输入中进行模糊测试的部分,然后选择格式和实际的输入列表。我们将使用包含常见 SQL 注入输入的字符串文本列表。你可以在网上找到许多这样的单词列表,复制粘贴作为你的有效载荷。一旦完成,你就可以准备好发起攻击了:
](image/B15632_04_045.jpg)
图 4.45 – Fuzzer 窗口准备启动
你可以插入任何输入列表,并且可以应用特殊编码。这用于演示,如果你想尝试更自定义的攻击。不过,记住扫描器模块已经使用常见输入进行了模糊测试攻击,用于识别常见漏洞。
在处理过 OWASP ZAP 之后,这个工具可以帮助你节省时间,快速检查 Web 应用程序并发现漏洞,相比于手动分析,节省了大量时间,现在我们将进入自动化 SQL 注入领域,使用最著名的工具之一 —— sqlmap 命令行界面。
使用 sqlmap 进行自动化 SQL 注入攻击
如前所述,sqlmap 是 Kali Linux 中包含的软件工具之一,是一款非常著名的工具。虽然 OWASP ZAP 是用于发现和分析 Web 应用程序中各种漏洞的工具,但 sqlmap 是专门为 SQL 注入设计的,并提供了许多用于此类攻击的选项。不过,它的用户界面并不友好(这也是大多数命令行工具的共同特点),因此最好使用完整的帮助(-hh)选项查看所有可用的选项。你可以在 Kali Linux 终端中输入sqlmap -hh
来查看:
](image/B15632_04_046.jpg)
图 4.46 – sqlmap 帮助输出
让我们再次针对 Peruggia 进行测试。首先,我们扫描添加评论页面,看看 sqlmap 是否发现该页面存在漏洞。我们将在终端中输入sqlmap -u "192.168.56.101/peruggia/index.php?action=comment&pic_id=1"
。sqlmap 会询问我们是否要在执行过程中尝试不同的攻击技术。由于我们正在扫描网页,重点是检查是否有攻击有效,因此我们将根据请求选择是否执行所有可能的攻击,回答Y
或N
。几秒钟后,我们将得到最终结果:
](image/B15632_04_047.jpg)
图 4.47 – sqlmap 基本扫描结果
在这里,sqlmap 已确认 Peruggia 的 添加评论 页面存在 SQL 注入漏洞,既通过基于时间的盲注,也通过 UNION
查询。此时,我们知道该参数存在漏洞,可以进一步挖掘。
现在,我们将使用 sqlmap 进行数据库枚举。首先,我们将尝试从服务器获取所有数据库。为此,我们需要在终端输入以下内容:
sqlmap -u "192.168.56.101/peruggia/index.php?action=comment&pic_id=1" –-dbs
运行命令后,我们将得到结果。这确认了我们手动枚举的尝试,通过列出服务器上所有的数据库:
](image/B15632_04_048.jpg)
图 4.48 – sqlmap 成功的数据库枚举尝试
现在,我们可以选择一个数据库进行进一步的探索。我们可以通过运行 sqlmap 并使用 sqlmap -u "192.168.56.101/peruggia/index.php?action=comment&pic_id=1" –-tables -D peruggia 来提取其中一个数据库(Peruggia)中的表:
](image/B15632_04_049.jpg)
图 4.49 – sqlmap 提取属于 Peruggia 数据库的表
此时,由于我们已经得到了表,我们可以继续提取表中的所有信息。我们将使用 sqlmap 的转储功能,该功能将提取表的完整内容。为此,我们需要在终端中使用 sqlmap -u "192.168.56.101/peruggia/index.php?action=comment&pic_id=1" –dump -D peruggia -T users 来完整提取 users
表。sqlmap 还内置了一个密码破解模块,用于检查密码哈希值,我们将在这里使用它。
最终结果,包括从存储的哈希值中获取的密码,将以类似表格的格式显示在输出的末尾:
](image/B15632_04_050.jpg)
图 4.50 – 从 Peruggia 数据库转储用户表,包含密码
当然,除了支持 HTTP GET
请求(如本例所示),sqlmap 还支持带有 --data
选项的 POST
请求。通过这种方式,我们还可以攻击包含表单的网页。我们将尝试一个由 Mutillidae II
应用程序提示部分建议的攻击负载,以便以简单且可复制的方式展示其功能:
sqlmap -u "http://192.168.56.101/mutillidae/index.php?page=view-someones-blog.php" --data="author=6C57C4B5-B341-4539-977B-7ACB9D42985A&view-someones-blog-php-submit-button=View+Blog+Entries" --level=1 --dump
--data
选项后跟数据,数据会传递到表单中作为请求的一部分。这将导致与之前针对 GET
请求进行的攻击相似的结果。可以通过检查有效请求来提取 POST
参数,并将其插入为数据。然而,需要警告的是:由于参数众多,这种攻击可能比之前的攻击花费更多时间。
sqlmap 结果,包括日志和转储文件,始终保存在文件系统中的一个文件夹中,该文件夹位置在 sqlmap 输出的末尾指定(通常是 /home/
图 4.51 – 在 CLI 文本编辑器中显示的基于 POST 的攻击的 sqlmap 结果
数据库转储文件也以 CSV 格式保存,保持 SQL 特有的表格结构:
图 4.52 – 上次提取结果生成的转储文件
最终,sqlmap 是一个非常有用的 SQL 注入测试工具,提供了扫描可能的 SQL 注入漏洞、自动化数据提取的能力,甚至在某些情况下可以完全避免手动干预。数据也会方便地保存在文件系统中以供将来参考,同时内置的密码破解模块可以通过暴力破解方式,在运行时从存储的哈希值中破解密码。
这标志着我们对 SQL 注入 Web 应用程序测试之旅的结束。我们深入探讨了手动技术,同时也考察了自动化测试的可能性,展示了它如何通过节省宝贵的测试时间而显得如此便捷。
接下来,我们将改变话题,讨论如何将 SQL 注入扩展到其他不同于传统 Web 应用程序的环境中,并通过 Web 浏览器进行访问和探索。
攻击移动目标
移动应用程序顾名思义,是部分或全部驻留在移动设备上的应用程序。这意味着它们在方法和执行上与传统的 Web 应用程序有所不同。
在传统的 Web 应用程序中,我们的主要访问方式通常是通过 Web 浏览器。这样,整个界面会在浏览器中渲染,并通过服务器以 HTTP 响应的形式发送,响应中包含所有必要的内容,以按预期方式进行可视化,其中包括客户端代码(如 JavaScript)。
移动应用程序与可以解释任何 HTTP 响应的浏览器不同,它们有一个特定的客户端驻留在移动设备上。这个客户端已经包含了所有的图形和客户端代码。这意味着,在移动环境中,客户端和服务器之间的通信通常更轻量化,即只包含进行通信所需的最基本信息。这就是 Web 服务发挥作用的地方:它们代表了一种只交换应用程序运行所需信息的方式。
让我们看看 Web 服务是如何运作的。Mutillidae II 让我们可以在 Web 服务(SOAP
或 简单对象访问协议)环境中进行测试。只要我们以 Web 服务接受的格式发送数据,就可以执行应用程序的基本功能。让我们进入 Mutillidae II 中看到的用户查找(SQL)页面,并点击切换到 SOAP Web 服务版本按钮:
图 4.53 – Mutillidae II 用户查找页面;注意 Web 服务版本按钮
通过点击高亮的链接,我们将访问一个非常简洁的网页,网页上仅包含指向Web 服务声明语言(WSDL)的链接——这是我们 SOAP Web 服务的语言定义,以及它支持的功能:
图 4.54 – Mutillidae II 用户查找 Web 服务页面
通过点击每个功能,我们可以看到每个操作的输入和输出信息。我们可以使用根据WSDL
中指定的语言精心编写的请求与这些 Web 服务进行交互。例如,如果我们想使用getUser
功能进行交互,我们需要发送一个包含以下内容的请求体:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:ws-user-account">
<urn:getUser soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
</urn:getUser>
</soapenv:Body>
</soapenv:Envelope>
我们将尝试使用getUser
功能,通过插入一个自我证明的' OR 1=1 -- -
有效载荷作为用户名(在前面的请求中替换username_here),返回所有用户。我们应该得到与 Web 应用场景中相似的响应:
…
在这个示例中,我们看到 Web 服务,尽管使用不同的通信方式与传统的 Web 应用程序交互,但仍然可能容易受到 SQL 注入攻击。接下来,我们将探索这对移动应用程序意味着什么。
许多移动应用程序,像 Web 应用程序一样,依赖数据库来永久存储数据。其中一些应用程序在客户端本身内嵌有 SQLite 数据库。根据最佳实践,该数据库不应包含敏感信息,因为这些信息可以从设备中提取出来。我们更关注的是服务器存储的数据库。在这种情况下,它们的功能与 Web 应用程序完全相同,唯一的区别是发送和接收信息的方式。你猜对了:移动应用程序也可能容易受到 SQL 注入攻击。
我们已经准备了一个 Android 移动应用程序和一个简单的 Web 服务。我们将指导你完成导入和部署过程,以便你也可以用于测试。
首先,我们需要配置并运行 Web 服务:点击这里查看 Web 服务
-
从 GitHub 下载 Web 服务应用程序,使用这个仓库:https://github.com/PacktPublishing/SQL-Injection-Attack-and-Defense-Strategies。你可以在
C4
子目录中找到 Web 服务,位于MasteringSQLInjection-WebServices
目录内:图 4.55 – GitHub 仓库
-
打开 Eclipse,创建一个新的 动态 Web 项目(文件 | 新建 | 动态 Web 项目),然后选择 新建运行时 选项。通过选择 目标运行时 为
Apache Tomcat v9
来设置运行时环境。然后,点击 完成:图 4.56 – 在新的动态 Web 项目中创建服务器运行时
-
打开你下载到电脑的目录,进入
src
文件夹,将文件拖放到 Eclipse 的 项目资源管理器 标签页中的Java Resources
文件夹下的src
文件夹中。点击弹出窗口中的 确定:图 4.57 – Eclipse 中 Java 资源的 src 文件夹
-
双击 服务器 标签页中的服务器。然后,在弹出的配置中设置端口号为
8081
。按 Ctrl + S 保存设置:图 4.58 – 设置服务器
-
现在你已经有了代码和运行时环境,导航到 文件 | 新建 | Web 服务选项。确保在界面中将两个滑块分别调至 测试客户端 和 测试服务。其他所有设置保持默认:
图 4.59 – 设置 Web 服务 (1)
-
使用
Browse
按钮指向Service implementation
字段中的包以及实现类(com.packt.masteringsqlj.service.IOTMgmtServiceImplementation),然后点击Next
。再点击一次Next
:图 4.60 – 设置 web 服务(2)
-
稍等片刻,你将看到一个
Server startup
窗口。点击Start server
来启动服务器,然后点击Next
,在下一个窗口中选择Launch
。你的 web 服务现在应该启动:
图 4.61 – 服务器启动
加载并设置好 web 服务后,让我们通过 Android Studio 将应用加载到 Android 模拟器中:
-
你应该已经有了之前下载的代码。这一次,你可以在
C4
中的MasteringSQLInjection-AndroidApp
子目录下找到它。 -
打开 Android Studio。选择
Open an existing Android Studio project
。在提示时选择下载的文件夹:图 4.62 – Android Studio 启动说明
-
在新启动的 web 服务屏幕中,从
Actions
标签页中获取Endpoints
信息:图 4.63 – Web 服务的端点信息
-
将
Utils
类中的ENDPOINT
变量的端点信息复制过来。记得将localhost
改成你电脑的 IP 地址:图 4.64 – 编辑 Utils 类
-
通过点击运行/播放图标来启动应用,如下截图所示:
图 4.65 – Android Studio 中的运行图标
现在我们已经搭建好了环境,可以使用我们简单的应用并展示 SQL 注入在移动环境中的应用:
图 4.66 – 移动应用登录界面
该应用展示了一个相当简单的登录界面。我们可以用常见的有效载荷(' OR 1=1 -- -)尝试进行自反攻击。这将让我们获得访问权限。原因和我们之前看到的 web 服务示例相同;即,底层数据库(MySQL)没有应用任何安全措施,也没有对输入进行过滤。
现在我们已经演示了 SQL 注入如何影响移动应用,接下来我们将转向物联网环境。保持移动应用的运行,它将在我们下一个攻击场景中派上用场。
攻击物联网目标
在处理物联网设备时,我们通常考虑一个复杂的环境,这些设备通常位于互联网络的最外端。我们通常指的是低计算能力的小型设备——如传感器、小型家电等——这些设备通常运行嵌入式系统,功能极其简单。这是因为这些设备旨在执行非常专门的任务,这些任务不需要复杂的操作系统。结果是,形成了一些小巧、便捷的设备,这些设备始终在线,并且与其他设备进行通信,其他设备可以是小型物联网设备,也可以是服务器,这些设备可能会收集某种数据,无论是通过测量还是来自设备本身的输入。
物联网最近成为了一个热门话题,许多人正在投资这些实用的技术,这些技术帮助将科技融入日常生活中。然而,与此同时,这些系统中的安全性有时被忽视。这可能是由于这些设备的资源有限。这包括一些使用数据库信息的设备,可能会受到 SQL 注入攻击的影响。
在这个场景中,我们将使用我们的移动应用程序——与前一部分中使用的相同——通过运行在 Web 服务器上的数据库与(假设的)物联网设备进行交互。在物联网环境中,网络是分布式的,指令可能来自网络的不同部分,甚至是移动设备。事实上,应用程序在成功的管理员级别登录后,将允许经过身份验证的用户修改与该应用程序连接的物联网设备的状态:
图 4.67 – 我们应用程序的状态面板
在这个面板背后,当然有一个 SQL 查询。一旦查询被发送,我们可以像之前在 Web 服务场景中那样修改它。这次,我们将插入 ', status =(SELECT password FROM iot_mgmt_system.user WHERE username='admin' LIMIT 1) --
(别忘了末尾的空格)来编辑设备的状态,包含相关信息(在本例中,是移动应用程序的 admin
账户的密码):
图 4.68 – 通过我们的有效载荷更改的状态
物联网设备也可能以其他方式受到攻击。然而,大多数时候,这些攻击属于 Web 应用攻击的范畴——因为设备可能有 Web 接口用于与设置或配置交互——或者是其他传统手段,例如攻击开放端口和服务等计算机系统。
虽然我们的例子当然是一个简化的模拟场景,但信息始终是相同的:如果任何应用程序,无论是基于 Web 的、移动的,还是 Web 服务,在输入检查上没有做好充分的安全措施,那么底层的数据库就可能会不可挽回地被破坏。
想象一个场景,其中一个控制器向一个控制关键设备的服务器发送未经过滤的 SQL 输入,在更现实的场景中。如果恶意用户能够完全修改数据库,完全破坏其控制功能怎么办?以这种方式,损害可能扩展到现实世界,因为物联网(IoT)可以负责执行关键环境中的任务,比如智能城市、监控(例如,摄像头)、关键基础设施的智能计量(例如,水分配)或医疗设施。
重要提示
仅在受控环境中练习这些技能,不涉及第三方。未经目标所有者同意,使用安全工具和攻击技术是非法的,因此,如果你在自己不拥有的网站或系统上尝试这些技术,可能会让自己陷入麻烦。
总结
所以,我们现在来到了这个漫长而实用章节的结束。我们探索了通过利用与 SQL 数据库交互的易受攻击应用程序组件,可能发生的许多不同场景、应用和攻击。
Mutillidae II 给我们展示了通过 SQL 注入可能发生的基本攻击。此外,Magical Code Injection Rainbow 给我们带来了一些挑战,激发我们思考(你可以通过应用迄今为止学到的知识来解决这些问题),有时还会有一些变化。最后,Peruggia 帮助我们将知识应用到一个伪现实的环境中。
在处理手动 SQL 注入攻击后,我们了解了使用常见软件工具自动化 SQL 注入的可能性,包括扫描和攻击。我们通过 OWASP ZAP 的 Spider、Scan 和 Fuzz 模块以及 sqlmap 看到这一点。我们展示了如何大幅减少手动干预,提高攻击者和安全测试人员的效率(再次证明了确保 Web 应用程序安全的重要性,这些应用程序在几秒钟内可能会被攻破)。
最后,我们看了简单的 Web 服务和移动应用程序,在这些应用程序中,SQL 注入漏洞的影响远远超出了传统 Web 应用程序的范围,涵盖了从移动应用程序到甚至 IoT 设备,只要它们涉及 SQL。
在下一章中,我们将更具体地了解如何保护 Web 应用程序,如何通过各种措施来阻止 SQL 注入攻击。我们已经通过 Magical Code Injection Rainbow 看过一些示例,因为一些早期的挑战应用了一些(不完全的)措施。我们将学习如何通过正确的安全措施来防止此类攻击,如果做得正确,安全措施可以有效地防止这类攻击。
问题
-
在进行盲注 SQL 注入时,为什么二分查找会有用?
-
你知道通过 SQL 错误进行数据提取的方法吗?
-
哪些 OWASP ZAP 工具可以用于自动化 SQL 注入?
-
sqlmap 能否用来从哈希中提取密码?
-
SQL 注入仅限于 web 应用程序吗?列出本章中提到的所有目标类型。
进一步阅读
为了探讨攻击向量并进一步研究INSERT
、UPDATE
和DELETE
语句,我们建议参考以下资源:mysql 注入:更新、插入和删除语句的进一步研究。
-
osandamalith.com/2017/02/08/mysql-injection-in-update-insert-a
nd-delete/ -
osandamalith.com/2014/04/26/injection-in-insert-update-and-delete-state
ments/ -
https://osandamalith.com/2017/03/13/mysql-blind-injection-in-insert-and-update-statements/
第五章:通过防御性解决方案防止 SQL 注入
到目前为止,我们一直关注 SQL 注入的进攻性方面。在前几章中,我们看到恶意用户如何执行主要的攻击技术,以及成功的 SQL 注入攻击可能带来的后果。从一般意义上讲,我们看到 SQL 注入原理上是如何轻松地导致数据库完全被攻破的,这可能泄露敏感信息,赋予攻击者对连接应用程序的完全访问权限,或者完全破坏数据库、应用程序、网络服务,甚至连接设备的功能,无论使用何种技术。
在本章中,我们将更多关注防御方面的内容;既然我们已经知道存在如此强大且具有破坏性的漏洞——而且原理上它是如此容易被利用——我们该如何阻止它呢?这是我们在这里尝试回答的问题。显然,解决这个问题并不简单,通常需要同时采取多种防御措施。我们将逐一介绍最重要的防御措施,探讨在保护与数据库交互的应用程序时,通常需要注意的差异。
本章分为以下子章节:
-
理解一般性弱点和 SQL 注入的促成因素:在简要回顾使 SQL 注入成为可能的一般性弱点之后,我们将分析问题的本质,以便深入探讨其根本原因。
-
处理用户输入:每次 SQL 注入背后,都会有一些来自恶意代理的输入,试图篡改数据库查询,以执行通常在正常应用程序中无法进行的操作。因此,我们需要一种方法来处理用户如何与发送查询到数据库的应用程序进行交互。
-
输入清理与控制:当我们开始将用户输入视为不可信时,我们需要以安全的方式处理它。这是通过应用一些控制技术来实现的,这些技术可以从一开始就阻止 SQL 注入尝试,防止攻击者访问潜在的危险命令或指令。
-
防御 SQL 注入——代码层级防御:其中一些技术可以直接在开发应用程序代码时应用。我们将看到一些如何通过安全编码在设计阶段高效地追求安全的例子。
-
防御 SQL 注入——平台层级防御:除了在代码层面,其他控制措施还可以应用到应用程序流程的其他层面,利用“深度防御”概念,在多个交互阶段进行防护。
技术要求
对于本章内容,我们强烈建议先熟悉一下 SQL 注入场景中涉及的主要技术。除了阅读前几章中的技术要求部分,我们建议查看一些常见的与 SQL 结合使用的编程语言的文档,这样在讨论可以应用于应用开发的解决方案时,我们可以达成一致:
理解常见的弱点和 SQL 注入的启用因素
SQL 是与关系数据库交互的一个强大且有效的工具,因为它提供了通过各种功能和命令执行各种任务的机会。不幸的是,从安全角度来看,这种优势也可能成为负担;允许进行许多不同类型的操作意味着,如果没有适当的控制措施,任何人都可能将利用数据库的应用程序颠倒过来,使得攻击者能够根据恶意想象力做到任何事情,只有其他漏洞的存在才是攻击者能否成功的限制。
在上一章中,你亲眼看到了一个脆弱应用程序可能导致的后果(我们也希望你在过程中玩得开心),如果你已经读到本书的这一部分,你可能也会在想是否有方法能够提高安全性,防止这一切的发生。SQL 数据库今天仍然被广泛使用,所以你可以猜到,简短的答案肯定是有的。详细的答案是,这些防御措施需要一次性应用。
在探索魔法代码注入彩虹或甚至Mutillidae II
Web 应用程序的挑战时,我们看到了单纯的解决方案往往不足以应对,因为通常总有绕过它们的方法。然而,如果防御措施在应用程序的多个点同时实施,那么这些应急方法将不再有效,因为其他同时存在的防御措施将使 SQL 注入攻击几乎不可能发生,除非存在其他漏洞。
SQL 注入的主要问题在于用户输入如何与 SQL 的实际语法进行交互,主要是因为在代码层面上,SQL 语句通常是由文本字符串构建的。各种编程语言使用特定的函数,这些函数接受文本字符串作为参数。显然,这个文本字符串是用 SQL 语法编写的,以便作为 SQL 输入进行解释。以下是我们在第四章中使用的脆弱 Java Web 服务中的 SQL 字符串声明,攻击 Web、移动和物联网应用:
String query = "SELECT * FROM " + USER_TABLE + " WHERE username='" + user_id + "' AND password='" + password + "'";
query
字符串将作为命令通过 executeQuery(query)
发送给数据库,这是一个 Java 函数,在数据库连接内将输入字符串发送到数据库,以便进行处理。
你可以看到,虽然查询中的某些部分有固定的内容,像是由双引号界定的,但其他部分则由先前声明的变量组成。你现在应该已经能够察觉出我们要表达的方向,因为你已经看到了 SQL 注入的实际操作。通过将恶意负载插入查询结构中,攻击者实际上可以让查询执行任意命令,就好像他们自己在编写查询的一部分一样。在我们之前提到的攻击中,攻击者只需将恶意负载插入到user_id
参数的位置,改变查询结构即可。对于绕过身份验证的同值攻击,生成的查询将如下所示:
SELECT * FROM USER WHERE username='' OR 1=1 -- -'
AND password='password'
插入合法的 SQL 表达式可能会改变原本设计的查询功能,就像本例中通过使用字符串定界符和注释所做的那样。这就是为什么在安全方面,必须考虑用户输入。
以一种天真的方式处理这个问题可能会导致大量的攻击场景,这些攻击通过 SQL 注入实现,可能会对应用程序的完整性和安全性造成广泛的损害。因此,在处理来自应用程序外部的输入时,必须确保这些数据的安全性,并且始终要考虑最坏的情况——即这些输入可能来自恶意用户,或者已被故意篡改,目的是破坏应用程序的上下文。这就是为什么我们需要在讨论用户输入时,提到“信任”。
处理用户输入
在谈论安全时,什么是“信任”?实际上,这个概念在处理安全问题时非常重要,不仅仅是应用安全。
假设你正在街上走,突然有一个陌生人走过来问你路怎么走。你需要决定是否给这个人指路——当然,他可能是一个心怀不轨的人,想要攻击你偷走你的钱,但你可能会判断这个风险很低;毕竟,周围有很多人,你相信即使情况有些不对劲,你也能应付得了。于是,你决定在这个特定的情况下相信这个人。
当然,这个选择的明智与否取决于具体情况。假设你现在正在保护一个重要的能源厂区,突然一个人走过来,说他们忘记带一些重要文件想要进入厂区。由于你的职责是确保没有人未经授权进入厂区,你有义务核实这个人的身份,默认情况下,你应该不让任何人通过,除非你确认此人拥有根据权限允许进入的资格。
在处理安全问题时,第二种方法当然是我们应该复制的方式。零信任是这个游戏的核心。考虑到恶意用户也可以伪造身份,默认情况下,你不应该信任任何人。基本的假设是,每次都考虑最坏的情况;每个用户可能是恶意用户,因为你无法判断他们的意图,也不能排除这种可能。如果你采取相反的做法,信任每个人,那么一个恶意代理就足以让你的应用程序——甚至可能是你的整个 IT 基础设施——付之一炬。
在我们的例子中,这意味着需要对我们的应用程序和数据库进行防御,因为据我们所知,任何人都可能对我们发起 SQL 注入攻击——尤其是考虑到这种攻击的简单性。这些防御通常可以通过简单的清理和输入控制概念来总结。避免恶意输入的普遍解决方案实际上可以改变我们的应用程序行为,使得这些输入不会被应用程序解读为可能未曾设想的指令。
我们现在将探讨什么是清理输入并应用防御控制的含义,同时考虑在各个阶段可以采取的措施,来挫败攻击者可能进行的任何 SQL 注入计划。
清理和输入控制
我们看到所有 SQL(以及其他)数据库本身天生就容易受到 SQL 注入攻击,因为数据库唯一的作用就是接收指令。因此,我们需要在数据流的早期阶段采取措施,在查询实际到达数据库之前,防止注入攻击的发生。
这时,清理就发挥作用了。外部传入的输入会被清理掉任何可能导致危险指令的恶意元素。你可以将这个过程想象成强制要求每个人在进入公共泳池之前先洗个澡——你可以假设人们的个人卫生状况良好,但由于无法保证这一点,最好让每个人都洗个澡,这样可以弥补那些不洗澡的人。在大多数情况下,这可能不是必需的,但它确保了那些确实需要洗澡的情况被覆盖。
显然,清理并没有单一的方式,因为这些控制可以在应用程序流的不同阶段进行。然而,在大多数情况下,有两个主要领域可以应用这些防御:
-
应用程序编码:这就是应用程序功能实现的关键所在。大多数防御机制都属于这个领域,我们可以称之为代码级防御。通过对代码和信息处理的干预,大多数应用攻击都可以在这里被挫败,严格确保输入和命令按照我们希望的方式结构化和格式化。这可以通过转换输入、仅接受某些字符或输入长度,或动态生成查询来实现。如果做得正确,这通常可以挫败 SQL 注入尝试。
-
平台和基础设施配置:除了作用于应用程序代码外,安全控制还可以应用于应用程序所处的环境(从服务器和基础设施的角度来看)。这包括使用外部模块、设备和网络流量控制。虽然这对于安全应用程序代码而言可能显得过于复杂,但它通过阻止任何恶意输入到达应用程序,从而可以大幅减少成功攻击的机会,同时避免对你的应用程序或系统造成附带损害或其他类型的攻击。我们将这些机制称为平台级防御。
所有这些措施都是一种输入控制,因为它们代表了一种检查、分析和修改应用输入的方式,以使其在到达我们正在运行的软件之前变得无害或完全被阻止。
当然,仅仅应用一个控制机制并不能保证我们的应用程序能够抵御潜在的攻击,保持安全无虞。我们在第四章中已经看到,Web、移动和物联网应用攻击,仅应用一个控制手段可能不够。当我们查看 Mutillidae II 时,看到了一些简单的客户端控制措施。以下截图将提醒你,我们在尝试使用客户端控制的网页表单执行常规 SQL 注入时的情况:
图 5.1 – Mutillidae II 中的客户端控制
这种客户端控制仅仅防止了信息在字段为空时被提交。Mutillidae II 还采取了另一项客户端控制措施,即检查是否存在禁止字符——在此情况下是 SQL 注入启用字符(例如单引号和连字符——这叫做黑名单,我们很快会看到它的实际应用)。使用输入网页表单进行 SQL 注入尝试会失败,应用程序会返回一条带有 JavaScript 警报的信息:
图 5.2 – Mutillidae II 中的客户端控制信息
虽然这确实是代码级防御,但我们已经看到,仅靠客户端控制是没有用的,只要服务器端存在漏洞。我们在 HTTP 请求级别插入了恶意载荷,完全绕过了 Web 表单输入,忽视了这一防御。
这意味着什么呢?最终,最好的做法是应用多层防御,大幅度降低成功攻击的可能性。
在接下来的章节中,我们将看到这些防御机制如何在代码级别和平台/基础设施级别进行应用,了解可以用来保护应用程序免受 SQL 注入攻击的工具。
防御 SQL 注入——代码级防御
正如我们之前所说,正确实施代码级防御应该能够挫败所有恶意攻击者攻击你应用程序的计划。当然,错误总是可能发生,这就是为什么最明智的做法是同时应用多种防御机制。在本节中,我们将探索可以用来防止 SQL 注入攻击的主要工具。我们还将看到如何将这些控制措施在三种常见的 Web 应用程序开发编程语言中实现:Java、PHP 和.NET。
输入验证
输入验证是根据输入的内容来接受或拒绝输入的过程。我们只希望安全的输入被我们的应用程序处理,从而防止大多数攻击。因此,只有符合我们规则的有效输入才会被我们的应用程序接受和处理。
验证遵循两种主要方法,这两种方法在信息安全的其他领域也很常见:
-
黑名单:黑名单方法的核心是确定哪些输入是不允许的,并拒绝所有符合特定黑名单规则的输入。这种方法实施起来确实简单,但可能会在未考虑到的特定编码或发现新攻击方式时被击败。
-
白名单:白名单方法是黑名单的逻辑对立面;这些规则定义了什么是允许的,所有不符合该模型的输入都会被拒绝,只接受符合正确性规则的输入。它的实现可能更困难,但绝对值得,因为它能够抵御新发现的攻击,完全忽略应用程序本身未预见到的内容。毕竟,如果没有进行某种攻击尝试,用户为什么要输入一些不寻常的内容呢?
输入验证可能是防止恶意输入到达应用程序的最基本、最简单的方法,而且它是迄今为止最常见的防范手段。
Java 中的输入验证
Java 是一种非常灵活的语言,拥有悠久的框架开发历史,其中许多框架特别适合用于开发 Web 应用程序。由于可用框架种类繁多,我们将重点介绍最基础的部分。
正如我们之前所说,实现黑名单验证非常简单,你可以通过检查字符串是否包含敏感字符来完成。在以下示例中,我们为了简化,仅黑名单限制单引号和连字符字符。
以下代码检查输入字符串,该字符串作为名为 input
的变量传入,并通过调用名为 constructQuery()
的函数继续构建 SQL 语句,但只有当字符串不包含任何黑名单字符时:
String s = input;
if(s.contains("'") OR s.contains("-")){
throw new IllegalArgumentException("非法输入");
} else {
constructQuery(s);
}
白名单在编码上可能会稍微复杂一些,因为我们需要确切知道什么是合法的输入。如果我们期望输入的是名字,可以使用基于字母表的表达式:
String s = input;
if(s.matches("[[A-Z][a-zA-Z]*]"){
constructQuery(s);
} else {
throw new IllegalArgumentException("非法输入");
}
你会注意到,内部逻辑是黑名单的反向版本;只有当我们可以确定输入是合法时,才接受它。
当然,针对 Java 代码应用输入验证的方式还有更多精细的方法,它们通常依赖于特定框架的使用。你可以查阅框架的文档,但原理总是相同的,你会发现自己很容易理解其工作原理。
PHP 中的输入验证
至于 PHP,与 Java 类似,具体实现取决于使用的框架。与 Java 相似,它提供了一些有用的函数,可以帮助以简单的方式实现输入验证。其函数之一是 preg_match(regex, string)
,它与 String.matches()
函数类似,用于检查 string
字符串是否符合正则表达式 regex
模式。当然,这可以用于黑名单和白名单的实现:
$s = $_POST['input'];
if(preg_match("/'/", $s) OR preg_match("-", $s)){
// 验证失败处理
}
现在,对于白名单的情况,我们保持相同的结构,但以逻辑上反向的方式进行,因为 if
现在检查是否与正则表达式不匹配:
$s = $_POST['input'];
if(!preg_match("/[[A-Z][a-zA-Z]*]/", $s){
// 验证失败处理
}
最终,除了不同的语言外,Java 和 PHP 的机制基本相同。
.NET 中的输入验证
与 Java 和 PHP 不同,ASP.NET 有多种内置控件,用于开发应用程序。一个简单的控件是 RegularExpressionValidator
,它与我们在 Java 和 PHP 中看到的模式匹配函数采用相同的方法。该控件强制执行服务器端和客户端的验证。
在下面的示例中,我们应用了与前两个代码示例中相同的白名单方法,通过正则表达式进行匹配,只允许一个字母字符串,第一个字母为大写:
<asp:textbox id="input" runat="server"/>
<asp:RegularExpressionValidator id="inputRegEx" runat="server"
ControlToValidate="input"
ErrorMessage="参数必须只包含字母,第一个字母必须是大写字母。"
ValidationExpression="[A-Z][a-zA-Z]*" />
ASP.NET 也有其他内置控件,但这个通常是最有用的,因为它可以原生地根据正则表达式验证输入。
参数化查询
另一种防止 SQL 注入的方法是使用所谓的参数化查询。其主要原因是输入永远不会以原始形式(即字符串形式)直接发送到数据库,就像动态字符串构建中那样;而是被序列化并存储在独立的参数中(因此得名)。
这是通过在构建 SQL 语句时使用变量,使用标识符作为占位符,使得实际字符串能够安全地构建。通过使用现代编程语言中大多数数据库系统交互的 API,这一过程变得更加简便。
然而,值得注意的是,单独使用参数化查询并不意味着应用程序就不会受到 SQL 注入的攻击;有时,参数也可以包含存储过程,而存储过程如果存在漏洞,仍然可能导致 SQL 注入。这只是将防御机制结合使用的又一个原因。
参数化查询的另一个好处是,能够轻松地将已经存在的动态字符串 SQL 查询转换为参数化查询。我们将在 Java、PHP 和 .NET 中看到如何实现这一点,首先从一个常规(易受攻击的)SQL 查询构建开始:
User = request("username")
Pass = request("password")
Query = "SELECT * FROM users WHERE username='" + User + "' AND password='" + Pass + "'"
Check = Db.Execute(Query)
If (Check) {
Login()
}
现在让我们来看一下如何将前面片段中的查询进行参数化。
Java 中的参数化查询
在 Java 中处理任何数据库时,最常用的框架之一是 Java 数据库连接(JDBC) 框架。它是原生提供的,支持独立于所使用数据库技术的数据库连接,提供连接数据库的有用功能。其中之一就是 PreparedStatement
类,它允许使用以下代码:
Connection con = DriverManager.getConnection(connectionString);
String query = "SELECT * FROM users WHERE username=? AND password=?";
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, user);
ps.setString(2, pass);
rs = ps.executeQuery();
基本的 SQL 语句通过将值替换为问号来修改,然后通过 PreparedStatement
实例 ps
引用这些问号。然后,setString()
方法将按原始查询 query
中出现的顺序(1
对应 user
和 2
对应 pass)将这些值插入到问号的占位符中。最后,基于准备好的语句调用 executeQuery()
方法。
PHP 中的参数化查询
PHP 也有几个框架可以实现参数化查询。其中一些框架在 PHP 中可用。
我们将使用 PHP 数据对象(PDO)框架修改原始代码,因为它与 Java 中的 JDBC 框架在兼容性和功能上是直接对应的。它从 PHP 5.1 版本开始被包含在内:
$query = "SELECT * FROM users WHERE user=? AND pass=?";
$stmt = \(dbh->prepare(\)query);
$stmt->bindParam(1, $user, PDO::PARAM_STR);
$stmt->bindParam(2, $pass, PDO::PARAM_STR);
$stmt->execute();
这段代码几乎与 JDBC 示例完全相同;语句是从包含问号形式占位符的 SQL 查询 query
中准备的。然后,bindParam()
函数将输入参数绑定到问号实例,按数字顺序(1
对应 user
和 2
对应 pass),并指定参数的数据类型(PDO::PARAM_STR
定义了一个字符串参数)。最后,使用 execute()
执行准备好的语句。
.NET 中的参数化查询
至于 .NET,ADO.NET 框架提供了一种实现参数化查询的方法。这个名字来源于其基础的前身技术——ActiveX 数据对象(ADO)。
ADO.NET 通过使用数据提供程序与数据库进行交互,每个支持的数据库系统都有一个相应的提供程序。由于每个提供程序的代码语法不同,因此我们将展示使用 System.Data.SqlClient
提供程序的示例,它适用于 Microsoft SQL Server,以及适用于 Oracle 数据库的 System.Data.OracleClient
。首先我们来看一下使用 SqlClient
时的代码:
SqlConnection con = new SqlConnection(ConnectionString);
string Query = "SELECT * FROM users WHERE username=@user" AND password=@pass";
cmd = new SqlCommand(Query, con);
cmd.Parameters.Add("@user", SqlDbType.NVarChar);
cmd.Parameters.Add("@pass", SqlDbType.NVarChar);
cmd.Parameters.Value[„@user"] = user;
cmd.Parameters.Value["@pass"] = pass;
reader = cmd.ExecuteReader();
这里的参数是通过 @
字符引用的,它们通过 Parameters.Add()
被添加到 cmd
准备好的语句中,这会传入参数名称和类型(在这种情况下,是由 SqlDbType.NVarChar
表示的字符字符串)。OracleClient
的工作方式类似:
OracleConnection con = new OracleConnection(ConnectionString);
string Query = "SELECT * FROM users WHERE username=:user AND password=:pass";
cmd = new OracleCommand(Query, con);
cmd.Parameters.Add("user", OracleType.VarChar);
cmd.Parameters.Add("pass", OracleType.VarChar);
cmd.Parameters.Value["user"] = user;
cmd.Parameters.Value["pass"] = pass;
reader = cmd.ExecuteReader();
这个结构几乎与 SqlClient
示例相同。唯一的区别在于参数的引用方式(在查询语句中使用分号,而在其他地方不使用特殊字符)和对象名称。
字符编码与转义
应对恶意输入导致 SQL 注入的另一种流行方法是使用特定的字符编码和转义技术,这样使能字符就不会发送到数据库,从而防止了最常见的 SQL 注入攻击。有时,其他防御措施无法应用——例如,在一些预期姓氏的数据库中,因为某些姓氏可能包含撇号,比如 O'Malley 或 O'Brian,尽管它们仍然会被编码为单引号。在这种情况下,没有其他方法可以允许这些姓氏出现在你的数据库中。
然而,这次我们并没有在输入层面进行相同级别的清理,而是更关注清理输出,以便 SQL 语句中不包含危险字符。我们的目标是避免未清理的语句在应用程序流程中传输并最终到达数据库系统。
当然,这些技术因数据库系统的不同而有所差异,因为它们之间的语法存在差异。我们将看到这些技术在 MySQL、Microsoft SQL Server 和 Oracle 数据库中的应用。
MySQL 中的字符编码与转义
由于 MySQL 使用单引号作为字符串终止符,因此在构造 SQL 语句时,包含在字符串中的单引号需要进行编码。这可以通过将单引号替换为两个单引号,或者通过使用反斜杠字符(****)来转义单引号来实现。
在 Java 中,可以使用一个简单的replace()
函数来将一个字符的所有出现替换为其他字符:
query1 = query1.replace("'", "'");
query2 = query2.replace("'", "''");
从 PHP 方面来看,mysqli
框架是一个专门用于与 MySQL 交互的 PHP 框架,它适用于 PHP 5.x 及以后的版本。有一个非常方便的函数叫做 mysql_real_escape_string( )
,它会自动在文本字符串中的单引号前加上反斜杠。这种转义方法同样适用于其他危险字符:
mysql_real_escape_string($parameter);
当然,PHP 中依然可以使用 REPLACE
函数:
SET @query1 = REPLACE(@query1, ''', '\'');
SET @query2 = REPLACE(@query2 ''', '"');
另一个需要注意的清理问题是,其他有害字符包括LIKE
子句中的通配符,这些通配符可以定义任意字符,可能会在恶意地添加到查询时导致自我证伪。最相关的通配符是%
字符,它对应于零个或多个任意字符的通配符。可以通过在其前面添加反斜杠来通过replace()
函数转义:
query3 = query3.replace("%", "%"); // Java
SET @query3 = REPLACE(@query3, '%', '\%'); // PHP
在将查询语句发送到数据库或应用程序的其他部分之前,您可以将此机制应用于任何可能危险的字符。
SQL Server 中的字符编码和转义
对于 MySQL 的假设和机制,同样可以应用于 SQL Server。因此,我们之前提到的 Java 和 PHP 函数都适用于抑制单引号字符。这一次,当我们讨论 SQL Server 时,我们还将考虑相应的 C# 代码,用于将单引号替换为双引号:
query3 = query3.replace("'", "''");
除了我们之前考虑过的 MySQL,SQL Server 也有一个ESCAPE
子句,可以在LIKE
子句中用于转义 SQL 查询中的任何字符:
SELECT * from users WHERE name LIKE 'a%' ESCAPE ''
上面的查询在LIKE
子句中转义了反斜杠字符,仅返回用户名为a%
的记录(前提是该用户名存在)。
Oracle 数据库中的字符编码和转义
在考虑与 MySQL 相同的假设时,Oracle 数据库通常也依赖于 PL/SQL 语言。它也有一个replace()
函数,可以如下使用:
query = replace(query, '''', '''''');
Oracle 数据库也支持ESCAPE
子句,用于LIKE
子句,就像 SQL Server 一样。
在处理了这些特定技术后,现在我们可以转向更高层次的代码级对策了。
安全编码实践
在大多数情况下,所有应用程序安全问题的根源都存在于设计和开发阶段。事实上,设计师和开发人员往往不会在他们所工作的应用程序中考虑安全方面的内容,通常更关注功能方面。这不仅导致了像 SQL 注入这样的漏洞,也导致了这些漏洞的修复变得越来越困难。在设计和开发阶段解决安全问题所需的努力要少得多,因为所要做的只是应用我们之前描述的工具和一些安全设计原则。
OWASP SAMM 框架
OWASP 及其他相关项目已经制定了一个重要框架,为组织提供工具,以确保应用一个促进安全软件开发的模型,涵盖软件设计和开发过程中所有阶段和利益相关者——软件保障成熟度模型(SAMM)。该框架为企业提供了一种自我评估其软件开发生命周期中安全性的方式,无论所使用的技术如何。欲了解更多信息,您可以访问该项目的网页:owasp.org/www-project-samm/
。
现在我们将考虑一些有助于防范 SQL 注入的良好实践。这为从编码角度处理 SQL 注入提供了一种以安全为重点的方法,介绍了一些可以证明特别有效的安全编码方面,从而节省在应用程序生命周期后续阶段中的时间和精力。
引入额外的抽象层
当我们谈论抽象层时,我们指的是不同的逻辑组件,每个组件都旨在与您的应用程序逻辑进行交互。应用程序层的常见例子包括表示层,它包括应用程序中更具图形性和交互性的方面,以及数据访问层,它旨在与数据进行交互,独立于核心应用程序逻辑。应用程序中分离不同层通常会提高安全性,因为从一层到另一层的迁移通常会受到更多的控制,并使得应用安全措施的实施更加线性和实际。
一个为安全原因引入的特殊附加层的例子是我们之前看到的 ADO.NET 框架,它通过引入一个附加层来与数据库进行交互,专门用于向数据库发送安全命令,类似于一个专用的数据访问层。
安全地管理敏感数据
设计安全应用程序时的另一个重要安全原则是决定如何管理和处理潜在的敏感数据。应用程序使用和存储的一些信息对于潜在的恶意攻击者可能具有极高价值,包括身份验证信息,如密码或信用卡号码,甚至其他敏感或个人身份信息,如姓名、姓氏、住址和社会保障号码。
在处理密码时,通常我们谈论的是特别重要的信息,这些信息通常是攻击者特别关注的目标。采取的最有用的措施之一是使用特别的安全哈希算法(SHA),例如 SHA-2,它提供了加密哈希中最高的标准之一,生成 256 位或 512 位长度的单向摘要。我们在上一章中看到,像消息摘要算法 5(MD5)等已被超越的标准不再安全,容易被攻破,攻击者可以在成功的 SQL 注入攻击中提取原始密码值。
保护敏感信息的另一种方式是通过屏蔽数据,只向应用程序显示部分数据,同时保持原始数据不变。这可以通过在应用程序中适当处理这些数据来实现,采用伪匿名化技术,例如,用特殊字符(如*****)替代数据的大部分,使其对信息拥有者可识别,同时又不会泄露超过必要的信息给外部人员。
当然,我们希望我们的应用程序尽可能对 SQL 注入免疫,但考虑到这个风险永远不可能为零,这种类型的保护无疑能在确保应用程序及其数据的安全性方面起到决定性作用,最大程度地减少成功攻击的影响。
存储过程
这个措施可能是迄今为止最具体的,因为它直接与 SQL 数据库相关。存储过程是存储在数据库本身中的特定指令,可以对其实施更严格的访问控制。
我们看到一个应用程序可能会访问整个数据库,所以当它遭遇 SQL 注入攻击时,攻击者可能会获得访问权限,甚至访问数据库中其他应用程序所在的同一数据库中的信息,就像我们在上一章中看到的,OWASP 损坏 Web 应用程序虚拟机的共享 MySQL 数据库,它允许访问属于其他应用程序的信息。
当使用存储过程时,你可以更改存储过程中的指令的访问权限,赋予执行的命令比应用程序更低的权限。这意味着要执行最小权限原则,即如果某种原因攻击者能够入侵包含 SQL 命令的存储过程,损害将会被限制,因为已经实施了更严格的访问控制。
以上内容总结了在应用开发和编码方面可以采取的措施。尽可能多地应用这些措施,肯定有助于保护你的应用免受 SQL 注入攻击。然而,这并不是你可以对应用程序采取的唯一额外安全措施,因为你还可以在应用程序运行的环境上下手。让我们看看这意味着什么。
防御 SQL 注入 – 平台级防护
如前所述,平台级防护指的是我们可以在平台和基础设施层面采取的所有安全措施,可能防止恶意命令进入或离开应用程序,并识别和阻止有害流量。这还包括对数据库系统本身应用安全措施。
在这里,我们展示了通过在应用程序外部应用安全控制和措施来保护应用程序免受 SQL 注入攻击的方法,从而提供额外的保护层。这个概念被称为深度防御,它是信息安全中最相关的方面之一,帮助最小化系统和应用程序可能面临的威胁。
应用级防火墙逻辑
第一个,也是最著名的保护概念是防火墙。一般来说,防火墙是一种根据某些特定规则决定是否允许数据流(通常是网络流量)通过的设备。在企业安全中,防火墙通常是位于网络和子网络边界的物理设备,充当网关。这些设备通常是专门用于过滤进入或离开网络的流量的加固计算机。
虽然传统的防火墙当然能帮助抵御任何类型的攻击,但我们更关注应用级的防护。通常,防火墙独立于网络中的应用程序存在,因此我们将它们视为相对于我们的范围的外部实体。然而,防火墙的相同逻辑也已应用于应用级概念,通过过滤指向应用组件的请求,方式与传统防火墙类似,丢弃任何根据一套规则被认为对指定应用组件有害的内容。让我们看一些这个概念的例子。
Web 应用防火墙
说到 Web 应用安全,几乎不提Web 应用防火墙(WAFs)几乎是不可能的。WAF 通常以软件解决方案或内嵌于特定网络设备中的形式存在,旨在保护 Web 应用免受各种可能的攻击,包括 SQL 注入攻击。最实际的解决方案是使用基于软件的 WAF。这些 WAF 通常内建于 Web 应用或 Web 服务器中,并且由于不会改变应用周围的 Web 基础设施,因此几乎不需要配置工作。另一方面,基于设备的 WAF 在某些情况下也能发挥作用,因为它们的活动不会占用 Web 服务器的资源,从而不会影响功能和性能。然而,由于应用开发者通常不愿意干预周围的基础设施,因此我们通常会讨论基于软件的 WAF,部分原因也是因为它们可以简单地实施。
WAF 通常通过使用定义允许和拒绝内容的过滤器来工作。这些过滤器作为 WAF 的规则,负责接受或拒绝请求。过滤器需要正确配置,以防止大多数针对应用的攻击。过滤器的实现方式有很多种:
-
Web 服务器过滤器:这些过滤器作为额外模块安装在 Web 服务器上,通常作为额外组件工作,评估进入 Web 服务器的请求。根据 Web 服务器技术的不同,实施方式可能有所不同。一些例子包括
ModSecurity
(可在modsecurity.org/
找到),它适用于 Apache,还有微软的UrlScan
,它专为 IIS Web 服务器设计(docs.microsoft.com/en-us/iis/extensions/working-with-urlscan/urlscan-3-reference
)。 -
应用过滤器:这些过滤器可以作为你应用的额外模块实现,通常使用相同的编程语言。对于应用开发者来说,这个选项通常被认为是可行的,因为这些过滤器通常与 Web 服务器技术无关,并且可以作为额外的应用插件加入。OWASP 的一个显著例子是 OWASP Stinger,但 OWASP 已经不再支持它。
-
Web 服务过滤器:另一个有用的选项是过滤 Web 服务消息。这可以通过定制方式进行,比如过滤包含 SQL 注入尝试的输入消息,或者过滤包含信息泄露的输出消息。
WAF 是一种非常多功能的工具,可以用在不同的模式下进一步提高安全性。
应用入侵检测系统
除了常规的基于网络的入侵检测系统(IDSes),这些系统可以用来识别网络攻击并提供警报功能,Web 应用防火墙(WAF)可以作为应用层 IDS,直接将这一概念应用于它所保护的特定应用程序。
其工作原理是将 WAF 置于被动模式,这样它可以检查应用程序请求,并在发现可疑请求时发送警报。通过这种方式,网络管理员可以在安全事件发生时及时收到警告,从而根据警报触发采取行动。
数据库防火墙
我们要考虑的最后一个防火墙是数据库防火墙。数据库防火墙基本上是一个代理服务器,位于应用程序和数据库之间,负责检查发送到数据库的查询。
应用程序以与直接发送到数据库相同的方式发送查询,但查询会被发送到数据库防火墙。在这一点上,数据库防火墙可以检查查询,查看是否存在任何异常(例如,是否包含偏离正常应用行为的语句、同义反复或任何指定的非法字符)。代理随后可以根据其评估规则决定查询是否可以到达数据库,从而避免数据库接收到有害的查询。
由于数据库通常由应用程序调用以执行由应用程序需求定义的一组特定功能,因此建模白名单规则是最好的方法,只允许被接受的查询通过代理。
数据库服务器安全机制
现在我们已经看到通过阻止恶意输入和输出数据来保护应用程序边界的方法,唯一需要保护的元素就是数据库服务器本身。我们可以将迄今为止所学到的一些概念应用于数据库的安全保护。
保护数据库数据
除了应用我们已经看到的措施——比如哈希密码和屏蔽敏感数据——确保数据库本身安全的最显而易见的步骤就是对存储的数据应用加密。加密确保了如果直接读取数据库(例如,如果数据库中的数据以某种方式被复制或转储),其内容仍然受到保护,无法被恶意攻击者读取。
加密本身无法提供 100%确定性,确保数据永远不会被未授权的个人或组织读取,但它保证了要破解这种保护需要足够长的时间和大量的计算能力,从而使得这些攻击变得不切实际或几乎不可能。
加密算法随着时间的推移不断发展,并且每当发生重大技术突破并且个人和组织拥有更多计算能力时,它们可能会变得过时。这种情况在较旧的加密标准中已经出现过,比如数据加密标准(DES),这种标准已使用多年。然而,随着普通计算机性能的不断提升,其加密方式被认为不再安全。其他更可靠的标准,如 3DES(DES 算法的三重迭代),现在也由于相同的原因被认为不安全。虽然几年前它们提供了足够的安全性,但一些攻击者可能拥有足够的计算能力来破解它们,从而能够访问受保护的信息。
现代加密学的事实标准如今是高级加密标准(AES)——特别是使用 256 位密钥的 AES-256,它提供了很高的安全保证。它作为对称加密算法工作,因为加密和解密数据都使用相同的密钥。只要密钥保持秘密,所加密的信息将继续受到保护。为了让这个安全特性更有感知,破解 256 位密钥的暴力破解方法,需要尝试所有可能的组合,可能需要高达 2 的 256 次方次的尝试(结果是一个 78 位数字)。即使每次尝试只需要一纳秒(十亿分之一秒),破解所有可能的组合所需的时间也超过任何 68 位数的秒数。如果使用程序来破解加密,它运行的时间将远远超过太阳的预期寿命,而太阳的预期寿命大约是 50 亿年。
通过加密技术保护数据的唯一挑战在于保持加密密钥的秘密性。这远非易事;如果密钥直接存储在数据库服务器上,它可能会被攻击者读取。一个可行的解决方案是将密钥安全地存储在不同的位置——例如,存储在应用服务器的安全位置。为了使用密钥,攻击者需要同时攻破应用服务器和数据库服务器。
使用适当的机制对数据库中的数据进行加密,应当能够在原始数据被外泄或存储数据的实际设备被盗时,提供足够的安全性,从而保护信息的机密性。
保护数据库服务器
在对存储的数据进行操作之后,接下来我们来看看如何保护数据库服务器。数据库服务器首先是网络中的一个系统,因此它可能天生就容易受到网络攻击。有许多方法可以防止或最小化恶意行为的影响,以下是一些示例:
-
修补:数据库服务器是你基础设施中的一部分。因此,你需要确保它的漏洞最少,以便攻击者能够通过最少的方式对其进行破坏。一个基本的安全原则是确保运行在数据库服务器上的软件始终保持最新,并安装了最新的安全更新。
大多数更新的发布是为了修复漏洞,其中一些漏洞对服务器及其周围基础设施构成严重风险。因此,安装安全更新至关重要,不仅要安装在数据库系统上,还要安装在操作系统和所有已安装的软件上。
漏洞实际上可能会被一个接一个地利用,更多的漏洞意味着系统被攻破的风险增加。修补不仅可以手动执行,还可以通过自动修补代理来强制执行,这些代理在各种企业网络中被广泛使用。
-
执行最小权限原则:我们在处理存储过程时已经讨论过最小权限原则。这一次,我们需要更广泛地讨论它。在操作系统上,程序可以在不同级别运行,从低权限到管理员级别不等。
提高安全性的一种方法是确保数据库程序在低权限级别下运行,包括读取、写入和执行权限。应用最小权限原则可以确保,即使数据库被攻破,由此导致的行动其影响也会被减轻——攻击者对系统本身造成损害的机会较小,并且可能仅能进行有限的横向移动(即攻击网络中的其他系统)。
-
执行身份验证和监控控制:最后,防止攻击者造成损害的另一种方法是通过改善与身份验证和监控相关的安全控制。这包括确保密码不弱,通过实施强密码策略、禁用默认帐户(默认帐户通常是攻击者的目标,因为他们已经知道用户名,只需要猜测密码)以及启用日志记录,以便跟踪可能的身份验证尝试和服务器本身的操作。
这部分总结了在平台级防御中可以采取的更实际的措施来防范 SQL 注入。除了这些,值得注意的是在保护应用程序时需要考虑的一些更一般的原则。
其他安全措施
除了我们到目前为止所看到的,基础设施和平台级的防御还可以包括一些其他通用原则,如果遵循这些原则,可以进一步提高你的环境的整体安全性。一般来说,应用我们到目前为止所看到的所有措施肯定能使你的应用程序和系统达到一个相当令人满意的安全水平,但为了完整性,我们现在将列出一些可能的调整措施,提供额外的 SQL 注入防护。
降低信息泄露
在对应用程序或系统进行攻击时,恶意代理总是会尽其所能尽可能多地获取关于你环境的信息,以便根据获得的信息尝试各种攻击。限制他们可以访问的信息可以有效减少他们的攻击潜力,从而最大限度地降低你的应用程序被入侵的风险。在这里,我们将介绍一些减少泄露信息可能有用的领域,并有效限制攻击者潜力的方式:
-
不显示错误信息:当我们处理攻击性 SQL 注入技术时,我们尽可能依赖错误信息。默认的 SQL 错误信息可能泄露关于版本和查询语法的信息,还可能为攻击者提供其他线索,尝试其他攻击。显示错误信息可能会给攻击者更多线索,比你可能希望的要多,因此避免显示错误信息是一个好主意。
有时,完全不显示错误发生的情况是一个明智的选择,可以避免可能的盲目 SQL 注入攻击。你可以选择在应用程序中根本不显示任何错误,或者提供一个通用的自定义 HTTP 错误页面(例如,HTTP 500 错误页面)。当然,这会使调试变得更加困难,但如果应用程序处于生产环境中,这应该不是问题。
-
防止 Google(以及其他搜索引擎)黑客攻击:Google 黑客技术是一种通过在 Google 搜索字符串中插入特定操作符来返回网站特定信息的方法。插入某些关键词可能使攻击者通过访问包含特定关键词的网页,获取与你的应用程序相关的信息。可以通过编辑你网站根目录中的一个文件来防止这种情况,指示搜索引擎不要抓取你的网站,从而避免内页被它们访问。这个文件叫做
robots.txt
,其内容如下所示,以防止这种行为:用户代理: *
禁止: /
这意味着不允许网络爬虫索引你的网站,从而防止通过特定的网页搜索显示可能被攻击者利用的内容,这些内容可能为攻击者提供有用的信息。
限制应用程序展示的信息量,仅限于严格必要的内容,能极大提升安全性,因为它能有效打击潜在攻击者尝试破坏应用程序的行为。
安全的服务器部署
另一个关键的安全步骤是服务器部署,部署时需要采取尽可能低风险的方法。
通常,最好将应用程序基础设施元素分开。这包括应用程序/ Web 服务器和数据库服务器。如果它们部署在同一台机器上,攻击者通过攻破其中一方,可能轻易地获得对另一方的访问权限。此外,这可能会削弱诸如加密等措施的作用,因为攻击者将同时访问数据库和存储的加密密钥。一般来说,将架构拆分成更多组件有助于通过减少可能恶意行为造成的影响来保持安全性。
部署时还应确保机器配置已被安全设置,移除开发阶段调试和测试时的典型配置。例如,一些用于调试和远程访问的开放服务可能为攻击者提供攻击点,攻击者可以利用这些点破坏您的系统和应用程序。
网络访问控制
最后,与为每个服务器使用单独机器配合的另一项安全措施是应用网络访问控制(NAC)。NAC 包括仅允许特定主机连接到特定服务器和服务。例如,在我们将 Web 服务器和数据库服务器分开部署的环境中,我们希望数据库服务器只接受来自 Web 服务器的连接。否则,攻击者如果成功入侵网络,可能直接与数据库服务器进行交互,从而绕过我们在应用程序层面所设置的大部分安全措施。
这可以通过例如仅允许具有特定证书的主机连接,或者通过使用防火墙,仅允许来自特定主机的连接来实现。路由器也可以通过实施访问控制列表来强制执行这一原则,提供一组允许接受连接的主机。
总结
这些信息确实不少。应对防御机制时,需要考虑许多因素,且应用于环境中的防御机制越多,攻击者造成损害的机会就越小。因此,使用本章中描述的所有安全措施——或者根据具体情况和这些控制的适用性,几乎所有的安全措施——对安全至关重要。
本章首先介绍了防止 SQL 注入的对策的基本方面——特别是如何处理用户输入和控制数据流。然后,我们分析了处理应用程序编码的具体防御措施,应用开发中的一般模式,以及保护应用程序周围基础设施的方式。
至于代码级防御,我们看到如何验证输入,使用黑名单和白名单方法,只接受安全输入。接着,我们应用了清理措施,不仅在构建查询语句时使用参数化查询,还利用字符编码和转义来避免可能导致 SQL 注入的有害字符。同时,我们也审视了安全编码实践,展示了构建防止 SQL 注入攻击的安全代码的理由,并且间接提高了应对其他攻击的能力。
平台级防御可能超出了应用安全的严格范围,涉及更多的安全原则。我们看到如何通过 WAF 和数据库防火墙将防火墙逻辑应用于应用组件。然后,我们分析了如何保护数据库本身,这是依赖数据库的应用架构中最重要的部分之一,既要考虑数据,也要考虑数据库服务器。最后,我们讨论了其他一般的安全措施,以提高应用程序的整体安全性。
虽然所有这些措施都有助于防止 SQL 注入,但你会意识到,重点通常更多地集中在一般安全性上。下一章将帮助你从更全面的视角审视你所学的内容,让你能够批判性地回顾所学的知识。可以把它当作一段长时间旅程后的回顾,让你有机会思考你所见所闻的所有内容。希望你不仅对 SQL 注入有更深的了解,也对一般安全性有了更广泛的认识。也许(仅仅也许)这还会激发你对更广泛的网络安全的兴趣!
问题
-
设计应用程序时,应该如何处理用户输入?
-
输入验证是什么意思?请描述两种验证方法。
-
什么是参数化查询?
-
为什么字符编码和转义对防止 SQL 注入有用?
-
WAF 的作用是什么?
-
将加密密钥存储在与加密数据相同的位置安全吗?
第六章:将所有内容整合
到这里,我们终于走到了探索 SQL 注入秘密的旅程的尽头。到现在为止,你已经体验过 SQL 注入是什么,它在应用程序或更复杂系统中的含义,它可能对安全性带来的后果,以及为减轻或完全防止其影响所能采取的对策。
本章是对你通过阅读本书所学内容的总体回顾。它将通过简要总结和分析我们所见的内容来实现这一点,希望能够从批判性的角度审视一切,同时考虑到不仅是 SQL 注入的更广泛影响,还有在一个始终依赖信息技术和数据的世界中,安全漏洞的一般性问题。
其目标不仅是帮助你以结构化且易于跟随的方式简要回顾本书的知识和实践内容,还通过批判性地审视这些概念,为你提供思考的空间,并赋予这些概念更深层次的意义。毕竟,本书的目的是掌握 SQL 注入,不仅从技术角度,还要确切地了解它的含义。
本章涵盖以下主题:
-
SQL 注入——理论视角:在这一部分,将总结 SQL 注入的理论方面,描述 SQL 注入背后的主要概念,并提供相关评论。
-
SQL 注入——实践视角:在这里,我们将简要描述并讨论更实用的方面,尤其是从含义和影响的角度。我们还将突出与现实世界相关的方面,不仅涉及 SQL 注入的测试和对策,还包括更普遍的漏洞问题。
-
SQL 注入和安全影响——最终评论:最后,一些额外的最终评论将帮助你专注于本书的真正目标,并理解成为网络安全专家意味着什么,以此激发你对这一诱人领域的兴趣。
SQL 注入——理论视角
总结我们在本书第一部分中讨论的所有理论方面可能看起来有些困难。在这里,我们将按照我们遇到这些内容的顺序概述我们所覆盖的内容。
一般的 SQL 注入
首先让我们回顾一下什么是 SQL 注入,以及它为什么存在。SQL 注入本质上是由 SQL 引起的,SQL 是一种负责与关系型数据库模型交互的语言。SQL 是一种非常强大的语言,能够执行广泛的操作,包括在数据库中创建(CREATE)和插入(INSERT)信息,删除(DROP
用于表和数据库,DELETE
用于单条记录),修改(ALTER)或者在应用程序中更常见的,只是选择和查询(SELECT)其内容,并提供许多不同的选项。SQL 注入允许恶意用户在现有操作中注入原本未被应用程序设计所预见的操作,可能会导致有害的命令。
SQL 注入的常见用途包括让恶意用户获取本应保密的信息(例如访问凭证或个人信息),以及直接利用应用程序逻辑,绕过身份验证检查,甚至无需插入任何凭证。这还包括在未征得所有者同意的情况下修改数据库,可能导致通过不可修复的方式破坏应用程序功能(例如,删除包含所有访问信息的表,或删除对应用程序正常运行至关重要的信息),从而使应用程序无法使用。
尽管存在不同的基于 SQL 的数据库系统(例如 MySQL、SQLite、Oracle 数据库和 Microsoft SQL Server),从用户的角度来看,它们的主要区别仅在于查询语法。在某些情况下,一些字符被保留用于不同的目的(例如注释),而某些内置函数可能会因实现不同而有所差异。无论如何,它们的功能背后的逻辑大致相同——可以将它们的查询语言视为同一种语言:SQL 的地方方言。
SQL 是应用程序与支持的数据库系统进行交互的主要工具。SQL 注入是一种软件漏洞,因为恶意负载是在应用层注入的,绕过了通常由软件允许的有限操作集。数据库只会评估发送给它的 SQL 代码,因此我们可以说,尽管 SQL 注入是由于 SQL 本身提供的可能性而变得可能,但这并不是一个数据库问题。相反,应用程序应该包含安全控制,确保在连接的数据库上执行的操作仅限于设计时定义的操作,符合应用程序设计要求。
一些设计原则,如果在与 SQL 数据库交互的应用程序开发中得到应用,可以避免 SQL 注入,这些原则通常涉及正确处理查询内容;例如,抑制危险的字符和命令。一般来说,应用程序应严格控制用户能够执行的操作。
SQL 注入攻击技术
就具体的攻击技术而言,SQL 注入可以为潜在攻击者提供多种方式来操作数据库并改变其功能。让我们逐一讨论最常见的几种方法。
破坏应用程序功能
攻击者可以通过 SQL 注入,利用连接任意 SQL 命令到现有查询字符串的方式,执行任意数据库命令,并通过分号来结束语句。一种非常简单但具有破坏性的情况是使用 DROP
语句,或者修改数据库中的信息,如登录信息,这可能对应用程序的功能至关重要。然而,这些完全任意的命令在大多数情况下是无效的,因为 SQL 通常一次只支持一个查询。多条查询(如内联查询)通常不被支持,从而限制了这种类型的攻击。
然而,如果应用程序已经支持可以修改数据库内容的语句,这些命令可能会被修改,导致应用程序遭受严重破坏。想象一下,如果一个原本打算删除单个用户的操作,结果却删除了数据库中所有的用户。那么,这种情况虽然不常见,因为设计良好的应用程序通常主要使用 SELECT
语句,但这些语句仍然有可能被利用。
使用 UNION
查询的 SQL 注入
UNION
是一种 SQL 子句,可以添加到现有的 SELECT
语句中,从另一个查询返回结果,并将其包含在第一个查询的输出中。要做到这一点,两个查询需要具有相同数量的属性。然而,攻击者可以通过添加任意的静态值,如固定数字,并采取试错法来轻松利用这一点。
使用 UNION
的 SQL 注入可以用来从一个易受攻击的数据库中收集大量信息。数据库模式本身,通常可以通过一些默认的表访问,能够查询到系统内的数据库、表格和表字段等信息,从而揭示数据库中包含的各种信息。这些结果可以直接用于查询数据库,为攻击者提供某种信息收集的蓝图,帮助其进行后续的攻击。UNION
查询的潜力因数据库而异。在某些情况下,它们可以用于检索比单个应用程序使用的数据库更多的信息,尤其是在 MySQL 和 MSSQL 中,由于许多数据库可以同时查询,如果目标系统托管了多个依赖数据库的应用程序。
提权
恶意用户还可以通过 SQL 注入来获得比通常情况下更高的权限,从而能够滥用本来无法访问的应用程序功能。
使用信息收集技术,如UNION
查询,可以让恶意用户从数据库中提取信息,这有时可能导致密码泄露。实际上,登录信息通常存储在数据库的特定部分。通常,密码信息以密码哈希的形式存储,但如果使用像 MD5 这样的弱哈希算法,密码攻击即使在离线状态下也可以将其解密。在某些情况下,身份验证可能通过直接包含一个始终为真的布尔表达式来绕过,这个布尔表达式用于登录过程中的逻辑检查。
盲 SQL 注入
最常见的 SQL 攻击技术之一被称为盲 SQL 注入。这个名字源于这样的事实:有时对数据库执行的操作不会在应用程序中返回数据库查询结果,因此攻击者只能自己猜测数据库的内容。所有不显示完整查询结果的技术,包括绕过身份验证的技术,技术上都可以包含在这个定义中,但这一类别中还有许多其他技术。
在某些情况下,尽管一个易受 SQL 注入攻击的应用程序不会显示实际的查询结果,但如果查询结果存在与否,可能会显示差异。这通常与满足特定的布尔条件相关联。在这种情况下,我们讨论的是基于布尔的 SQL 注入,因为攻击者可以利用这些信息,通过使用布尔条件来推断数据库内容。
然而,也有一些情况,应用程序在成功查询和失败查询之间没有任何差异。在这种情况下,使用基于布尔的 SQL 注入对攻击者没有任何价值。然而,恶意用户可能会找到方法来生成这种差异,最常见的方式是通过添加时间延迟来实现,当某个任意条件得到满足时。在这种情况下,我们讨论的是基于时间的 SQL 注入。
最后,盲 SQL 注入的另一种技术被称为拆分与平衡。其目标是检查使用等效的 SQL 查询时,SQL 代码是否被系统评估。在这种情况下,攻击者还可以在相同结构中插入任意子查询,确保语法正确,从而执行潜在的危险命令。
NoSQL 注入
最后,值得一提的是,尽管 SQL 注入是最常见的数据库注入案例,但在非关系型数据库中,这个漏洞也可能具有一定的意义。
实际上,尽管数据库并不总是依赖于提供与 SQL 相同可能性的查询语言,通常被认为比 SQL 本身更安全,但一些任意命令可能会在应用程序级别被注入。这些命令可能会被数据库评估,导致潜在的有害操作。
在 NoSQL 数据库的情况下,这被称为 NoSQL 注入。尽管我们讨论的许多技术并不涉及 NoSQL 数据库(例如,使用UNION
查询进行数据库转储和使用复杂的任意语句),但攻击者可以通过在应用程序中放置输入来随意更改一些命令语义。一般来说,恶意用户可以通过在参数中插入元素来篡改 NoSQL 数据库的语法,并欺骗底层数据库评估意外值,这可能导致有害行为。
SQL 注入和其他安全漏洞
SQL 注入确实是一个有趣的话题,特别是考虑到使其可能的内部运作方式,因为它揭示了一个更广泛问题。SQL 注入实际上是一种特定类型的软件漏洞,涉及与数据库以某种形式互动的应用程序。这种漏洞的存在会触发意外行为,可能导致损害性后果,不仅仅是对应用程序本身,更普遍地影响周围的世界。
让我们看一个例子。想象一个允许经过身份验证的个人访问敏感信息的应用程序,例如个人身份信息、物理地址,甚至社会安全号码或其他一个人通常不会向任何人透露的细节。通过 SQL 注入,攻击者已经获取了数据库中包含的所有信息,并将其删除。在这种情况下,不仅应用程序受损,还有所有这些数据所属的人。恶意攻击者可以在互联网上披露这种数据库的内容,将这些人暴露给任何可能利用这些信息的恶意人士,这可能导致从骚扰到欺诈,甚至直接迫害的后果。公共数据可以被任何人使用,无论他们的意图如何,因此保持数据安全,不泄霩比严格必要的信息更多,以避免这些后果是至关重要的。
在当今世界,数据始终是一项关键资产。许多人已经在不知情的情况下向互联网上的各种实体提供了他们的个人信息访问权限,这些实体主要通过定向广告来利用这些信息获得商业优势。这些信息也被用来引导人们访问他们可能喜欢的内容。
从某种程度上讲,这也可能被用来强加一些思想和观点给目标人群,从而间接控制他们的思维方式和行为。这只是一个例子,让你一窥数据的重要性,以及任何人如何将其视为宝贵,因此你永远不应该对任何人透露过多信息。从这个角度来看,允许攻击者访问故意保密的信息的漏洞,当然是更为关键的,应该让每个人都对此感到担忧。
还有许多其他漏洞存在,这些漏洞不仅可能对目标系统造成破坏性后果,还可能泄露保密信息。大多数漏洞(如果不是全部的话)是硬件或软件有时意外功能的结果。这可能意味着编码错误、程序内存管理问题、用于连接的故障协议,或一些小疏忽,这些都可能导致即使是关键性的问题。
保持所有软件和固件更新的原因之一是,通常情况下,更新是用来修复发现的问题的。这并不一定意味着更新后的软件没有漏洞,但它确保了已知的漏洞在那时得到了修复。由于在安全漏洞的情况下,我们讨论的是通常未曾预见到的问题,因此漏洞可能会长时间未被发现。
在这种背景下,信息安全的使命是通过识别漏洞、提供对策,并在系统周围设置保护层,来保护 IT 环境,防止安全问题的发生。信息安全是一个协作性的努力,所有相关方都会参与其中:漏洞一旦被发现,就会公开披露,以警告所有人。这当然原则上可能有利于恶意攻击者,他们可能会获取这些潜在漏洞的知识并试图加以利用,但相比漏洞长期保密、仅为某些攻击者所知且能不受干扰地加以利用,这一风险微不足道。
一些已知的未公开漏洞案例包括一些 Windows 漏洞,这些漏洞长时间未被披露,允许一些监控机构(我不会提及具体名称)在全球范围内拥有某种后门——一种特权访问方式。虽然这种行为可以进行争论,但制造商和系统管理员对这些漏洞一无所知的事实,在这些秘密信息不小心泄露给负责保护它们的人时,带来了极高的风险。最近的例子包括导致 EternalBlue 攻击可能发生的漏洞,该漏洞影响了 Windows 系统。
这种漏洞使得恶意攻击者能够利用 Windows 中服务消息块(SMB)协议实现中的漏洞,如果未打补丁,可能会导致在目标机器上执行任意命令。此攻击由美国的国家安全局(NSA)设计。关于此类攻击和其利用的漏洞的信息在 2017 年浮出水面,恰逢该漏洞被修补之后,但估计 NSA 在此之前大约已知晓该漏洞五年之久。该漏洞的保密性使得许多网络犯罪分子在修补之前的几年里就已经利用了这一漏洞,这也为用户提供了一个充分的理由,永远不要将漏洞信息保密——攻击者迟早会发现这个漏洞,而且他们绝不会公开相关信息。
信息安全的使命是我们在本书第二部分关注实际方面的原因:测试是发现漏洞并进行修复的关键元素。对策使得测试变得有意义,从而使漏洞能够得到有效修复。让我们再一次回顾这一实践部分,解释我们所做的事情以及背后的原因。
SQL 注入——实践中的视角
在实践部分,我们搭建了一个安全的环境,以确保我们的测试不会对外部实体造成问题——这样模拟就像是在测试一个属于我们的真实系统——专注于识别和利用 SQL 注入漏洞。在处理了实践部分中可能是最有趣的部分之后,我们描述了防止 SQL 注入发生的措施。
使用 SQL 注入进行攻击
让我们回顾一下我们在选择的目标上所进行的测试,并回顾我们实践中的技术。
手动技术
通过利用 OWASP BWA 项目,我们能够探索我们在理论部分中看到的大多数攻击技术。这是通过选择三个特定的网络应用程序来实现的,这些应用程序让我们可以尝试多种 SQL 注入攻击。
我们的第一个目标是 Mutillidae II 网络应用程序,它是一个用于测试各种已知漏洞的培训网络应用程序。其中也包括了 SQL 注入漏洞。我们学习了如何利用SELECT
语句从数据库中检索任意信息,以及如何通过INSERT
语句来创建应用程序中的帐户,从而使得可以通过这些帐户提取信息。这些帐户的资料被修改,以包含数据库中存在的私密数据。
对于第二个目标,Magical Code Injection Rainbow,我们探索了多个以挑战形式呈现的 SQL 注入练习。在这里,我们测试了盲 SQL 注入、基于错误的 SQL 注入等技术,并利用可以通过错误信息返回查询结果的函数。
最后,通过 Peruggia,我们查看了一个伪真实的 Web 应用程序,它故意存在漏洞,但没有任何提示或指南。它的一个漏洞就是 SQL 注入。我们看到 SQL 注入如何为恶意用户提供绕过登录认证的方式。这个技术还可以通过盲注 SQL 注入进行推断,因为只有在条件满足时才会授予访问权限。通过使用布尔检查,可以验证数据库中的信息。
通过这些手动技术,我们看到了 SQL 注入的潜力以及测试这种漏洞的方法,以评估应用程序的安全性。
自动化技术
我们可以使用的另一个工具是使用特定的软件工具,这些工具可以帮助自动化地验证一个应用程序是否容易受到 SQL 注入攻击,从而在测试过程中节省时间。
OWASP ZAP 是一个多功能的 Web 应用程序安全测试工具套件,包含多种工具。具体而言,Spider 工具帮助我们在应用程序中查找网页,并设置爬虫来探索页面中的超文本链接,从而发现包含 Web 表单的动态网页。另一方面,Scan 工具以自动化的方式针对动态页面尝试各种有效载荷。这有助于我们发现漏洞,依据 Web 应用程序的响应:如果输出与易受攻击的响应匹配,ZAP 会将其记录为漏洞。当然,这并非完全无误,有时会导致误报,但它确实提高了效率。另一个有用的工具是 Fuzzer 模块,它通过将一组预设值替换到目标参数中,自动发送 Web 请求,从而进行更有针对性的测试,使用用户定义的特殊有效载荷。
sqlmap 是另一个重要工具,凭借其选项,它可以帮助识别目标网页上的 SQL 注入漏洞。提供了各种自定义选项,可以实现多种不同的攻击技术,还可以生成数据库转储。sqlmap 还具有破解密码哈希的功能,这些密码哈希可以从数据库转储中获取。
这两个工具被全球的安全专业人员使用,可以帮助大大提高 SQL 注入测试的效率,减少时间消耗。当然,这些工具不能替代手动测试,但通常足以帮助我们判断一个 Web 应用程序是否容易受到 SQL 注入攻击。
针对 Web 服务和移动应用程序的 SQL 注入
最后,在我们的 SQL 注入测试中,我们增加了针对 Web 服务和移动应用程序的测试,这可以定义大量可能的场景。事实上,Web 服务可能负责基于 Web 服务的简单应用程序,这些应用程序包含轻量级逻辑。对于移动应用程序尤其如此,它们通常代表远程 Web 服务的接口,而物联网场景通常在实现上相当简单,鼓励低计算强度的设备通过这些服务进行交互。
我们研究了这些,旨在证明 SQL 注入不仅仅与 Web 应用程序有关,而是与任何依赖 SQL 数据库的应用程序相关。
在进行这些测试后,我们转向了 SQL 注入的防御方面,即评估可能的对策。
防御 SQL 注入
从防御的角度来看,通常来说,这一切都涉及对应用程序的输入和输出进行检查和控制。毕竟,攻击一个应用程序通常是通过发送恶意输入来实现的。正如我们在 SQL 注入中看到的,原则总是相同的。首先,来自用户的输入需要被视为潜在的恶意输入,这样我们才能开始思考可以应用的安全措施。
在本书中,我们根据防御机制的应用场景进行了讨论,并将其分为代码级别防御和平台级别防御,具体取决于我们是在处理应用程序的实际编码还是围绕其的基础设施。
使用代码级别防御防御 SQL 注入
在将防御机制应用到我们的应用程序代码时,这些机制可以根据它们的目标被分为不同的类别。
输入验证包括检查输入并验证它是否符合我们的规则。通常我们可以通过两种方式来定义验证规则。最简单的一种是基于黑名单,即如果输入属于我们认为潜在危险的输入集合,我们就不接受它。然而,虽然这种方法容易实现,但我们需要具体定义什么是危险输入,可能会错过一些危险的情况,特别是当发现新的危险时。另一方面,白名单是一种更严格的方法,通常也更安全:我们只接受属于已接受输入列表的输入,从而排除我们不认为正常的任何东西。
另一种正确处理输入的方法是以安全的方式构造查询语句。我们已经看到,SQL 注入的“魔力”通常发生在查询语句是使用用户提供的输入构建时,从而生成了意外的命令。为避免这种情况,我们可以参考参数化查询,其中输入被保存到特定的参数中,并且在我们实际将查询发送到数据库之前,添加了一个额外的步骤。这确保了,如果正确应用,用户输入不会被解释为查询的一部分,从而改变语法。
另一个选项是构建我们的代码,使其排除用户输入中的有害字符,甚至忽略它们。如果我们将某个字符转换为另一种编码,使我们的应用程序能够读取它而不对 SQL 数据库造成危害,这称为字符编码。字符转义是指我们插入转义机制,使得数据库忽略这些字符,即使它们原样插入到查询中。
除了这些技术外,在开发应用程序时,还有一些有用的原则需要牢记。这些原则包括使用各种抽象层来构建应用程序,例如将用户界面与实际应用程序逻辑分开,从而不直接访问更敏感的区域。
通过添加加密技术和数据掩码来安全地处理敏感数据也非常重要,因为这可以防止恶意代理获取可能导致损害的私人信息,不仅对应用程序有害,在某些情况下还可能对用户的隐私造成威胁。尤其在考虑隐私和数据保护相关法律时,这一点尤为重要。
最后,即使专注于 SQL,我们也考察了使用存储过程而不是在应用程序中构建查询。这可以确保这些操作可以按照数据库本身决定的权限级别执行,因为发送命令到数据库的应用程序通常具有较高的权限级别。这意味着如果应用程序被攻破(例如,通过 SQL 注入),攻击者可能会完全访问数据库。另一方面,限制存储过程中的权限可以将攻击者的潜在影响限定于严格允许的范围,从而避免意外结果。
这就是我们对可以应用于应用程序代码和设计的对策的概述。现在,让我们继续查看应用程序上下文相关的方面。
使用平台级防御来防止 SQL 注入
在谈到平台级防御时,我们需要超越严格定义的应用程序安全性,因为我们还可以处理更多的附带方面。我们的目标是通过应用外部措施来保护应用程序,从而限制攻击的潜力,并降低成功攻击我们应用程序的可能性。
这个防御类型的第一个例子是Web 应用防火墙,简称WAF
。WAF 是组件,通常是软件,能够接受或拒绝传递到我们应用程序的应用层请求。这类似于代码级别的输入验证,但发生在应用程序之外,从而防止恶意请求触及我们的应用程序逻辑,就像什么都没有发送一样。WAF 可以直接在 Web 服务器级别工作,通过处理发送到 Web 服务器的请求;在应用层,通过使用外部的应用程序模块,从而独立于服务器技术;以及在 Web 服务层,这在使用 SOAP 的 Web 服务时非常有用。
WAF 也可以作为被动模式使用,充当入侵检测系统。这样,WAF 可以持续监听流量,并且在发送到应用程序的内容异常时发送警报。这样,管理员可以在攻击尝试发生时及时采取行动。
防火墙逻辑也可以通过使用数据库防火墙在数据库级别应用。这些就像代理服务器,位于应用程序和数据库之间,检查本应发送到数据库的命令。如果是恶意命令,什么也不会发送到实际数据库,从而防止 SQL 注入等攻击的发生。
可以通过直接保护数据库来添加另一层安全性。这意味着通过加密或掩码来保护存储在数据库中的数据,并通过应用补丁和安全配置来保护数据库服务器,同时确保适当的身份验证机制。
最后,另一些可以在平台级别应用的安全原则包括避免不必要的信息泄露、抑制错误信息以及防止搜索引擎探索您的 Web 应用程序。
另一个重要原则是通过将应用程序逻辑与数据库以及前后端分开来安全地部署应用程序。使用网络访问控制(NAC)进行网络级身份验证也可以防止许多攻击,因为只有某些网络实体被允许在应用程序上执行敏感操作。它们还可以使用特定的证书或通过防火墙执行的网络规则进行身份验证。
现在我们已经浏览了这一路程中涉及的所有主题,让我们从应用安全性和计算机安全性的一般角度,审视这些主题在本书实际部分中的重要性。
管理漏洞和安全缺陷
要真正将我们所讨论的内容放入更大的视角中,我们需要关注复杂环境中安全问题和漏洞的整个生命周期,例如公司或大型企业。
一些安全专业人员的工作是发现网络基础设施中存在的漏洞和安全缺陷,包括资产——例如服务器或工作站——或更具体地说,在应用程序中。识别安全问题的最常见方式是进行漏洞评估,通过分析目标系统来查找是否存在已知的安全问题,例如系统配置本身的漏洞、缺少安全更新,或使用各种工具和技术进行扫描。这些活动的重要部分是测试这些问题,以验证这些漏洞是否真会被潜在的恶意攻击者利用。
我们在第四章,攻击 Web、移动和物联网应用程序中所经历的,正是模拟了安全专业人员的工作,负责识别安全缺陷——在本例中是应用程序——通过评估它们被利用的实际程度,从而正确评估其影响。当然,我们重点关注了 SQL 注入,但由于还有许多其他漏洞,这项工作可能相当复杂——但也充满了挑战,而且在某些情况下,非常有趣。这为你提供了新的挑战和思考不同难题的方式。
安全测试和发现安全问题显然是关于将有效的修复计划付诸实践,以便有效地解决这些问题。第五章,通过防御性解决方案防止 SQL 注入,帮助我们了解了如何修复 SQL 注入漏洞。根据具体情况,以及可能的工作量和时间限制,一个有效的漏洞修复计划可以通过应用最有效的修复措施进行优化,以便在最小的操作量下提供尽可能高的安全性。
在这种情况下,安全专业人员需要知道最有效的应对措施是什么,考虑到发现这些问题的环境,并确保这些行动的影响对组织是可接受的。虽然在大多数情况下,安全专业人员不需要亲自实施解决方案,但他们需要了解这些解决方案的内容,以便能够指导技术人员将这些变更应用于受影响的基础设施和/或系统。
本书的实践部分旨在为你提供一个关于如何应对漏洞的实用样本,特别是聚焦于 SQL 注入——从发现部分、通过测试评估损害可能扩展的程度,到实际修复漏洞,探索可以采取的各种对策。这为我们提供了具体的示例,特别是在处理常见编程语言时。
这样,通过扩展我们在这个特定案例中所遵循的路径,我们可以考虑安全问题的完整生命周期。首先,漏洞被发现——通常是通过自动化方式,正如我们在使用 OWASP ZAP 的扫描模块进行 SQL 注入测试时看到的那样。当然,单次扫描可能会识别出大量问题,这些问题需要在接下来的阶段逐一评估。
在第二步中,实际上测试了这样的漏洞——与许多其他漏洞一起——以评估自动化分析结果是否为假阳性,并查看此类问题的实际影响。通过参考我们在第四章中提供的教程,攻击 Web、移动和物联网应用程序,我们正是这样做的:我们知道这些应用程序容易受到 SQL 注入攻击,但我们研究了这些漏洞,看看它们可能导致什么问题。
实际上,应用程序可能确实存在漏洞,但利用该漏洞可能导致较小的后果——在这种情况下,变化的影响可能远低于 SQL 注入漏洞,但仍可能导致应用程序的完全妥协。SQL 注入攻击的可利用性可能取决于现有的对策,虽然这些对策未必同时存在,但即使部分存在,也能阻碍攻击者的工作。
最后,在漏洞测试完成后,可以考虑采取对策,同时要牢记任何已存在的防御机制。这需要对当前安全问题有精准的了解。因此,针对 SQL 注入攻击,我们展示了可以采取的最相关的对策,以保护应用程序免受此特定漏洞的影响。一个经验丰富的安全专业人士可以通过应用程序的行为来判断已应用了哪些对策,从而更好地建议可能的防御措施。
建议的防御措施出现在制定修复计划的过程中,目的是指导技术人员应该应用哪些防御措施。此时,通过这样的计划,就可以决定是否通过实施适当的防御机制来解决与安全问题相关的风险。或者,取决于风险的严重性,也可以选择接受这种风险,因此我们不必应用可能会因为操作上的权衡而变得过于苛刻的对策(例如,应用某个安全机制可能会要求关键服务器停机时间超过组织可容忍的时间)。
网络安全是一个不断发展的学科,需要专业人员时刻跟进最新的威胁、漏洞和风险。在这一实用部分中,我们希望能够让你体验到作为这一有趣领域专业人士的感受。现在,让我们再一次总结主要问题。
SQL 注入及其安全影响——总结评论
既然我们已经通过本书探讨了 SQL 注入,我们可以讨论现今 SQL 注入及其安全问题,同时考虑它在全球互联网安全方面的影响,以及在现实世界中的后果。
现今的 SQL 注入
SQL 注入确实是一个历史悠久且广为人知的漏洞,因此,通常在开发或发布新应用程序时会特别考虑,尤其是在全球互联网这样的 Web 应用程序中。大多数基础攻击通常是无效的,因为大部分常见的防范措施通常会应用于绝大多数案例,且许多具有内置控制的 Web 框架经常被使用。然而,仍然可能存在脆弱的应用程序,这通常是由于源代码中的错误或疏忽,或者是其他一些无法预见的情况。
根据 OWASP 在 OWASP 十大 Web 应用程序安全风险(2017 年最新版)中提到的内容,注入攻击是 Web 应用程序的最大风险因素,因为我们在本书中探讨的内容表明,其被利用的后果非常严重。SQL 注入当然属于这一类别,因为它仍然是恶意用户通过插入任意命令与应用程序的内部逻辑进行交互,从而利用 SQL 语言的表达能力的最常见方式之一。
SQL 和 NoSQL 注入实际上通常被视为此类攻击的第一个典型例子:它无疑是恶意用户针对 Web 应用程序最常尝试的攻击技术之一,因为它从战略角度和操作影响的角度来看,都能提供巨大的优势。尽管它声名狼藉,基于 SQL 注入的攻击依然时有发生。
最近的例子也进入了新闻报道。2014 年,据报道,一些网络犯罪活动——归咎于俄罗斯的网络犯罪团伙——通过各种 SQL 注入攻击获取了 12 亿对用户名和密码(来源:纽约时报,2014 年 8 月 5 日,www.nytimes.com/2014/08/06/technology/russian-gang-said-to-amass-more-than-a-billion-stolen-internet-credentials.html?_r=0
)。
最近,一位漏洞赏金猎人——一种在特定程序中寻找漏洞的安全专家,经得程序所有者同意,并在漏洞被成功利用时给予金钱奖励——发现了一个 SQL 注入漏洞,导致了 Starbucks 的一个财务数据库暴露(来源:《The Daily Swig – Portswigger 的新闻博客》– 2019 年 9 月,portswigger.net/daily-swig/sql-injection-flaw-opened-doorway-to-starbucks-accounting-database
)。尽管要利用这个漏洞需要更复杂的攻击手段——通过利用应用程序的其他弱点——这仍然表明 SQL 注入仍然是今天的一个问题,且可能带来较高的安全风险,可能暴露关键信息。
与 SQL 注入相关的其他问题也可能作为框架和软件中的漏洞存在。有时,一些新的漏洞影响了用于构建 Web 应用的软件,若被充分利用,通常通过复杂和不常见的攻击技术,可能导致使用这些软件的应用程序出现 SQL 注入问题;也就是说,如果没有采取进一步的对策。这些漏洞通常在发现后会被修复,因此,如果使用了外部软件组件,必须确保它们始终保持最新版本。
通过这一点,我们可以看到,尽管 SQL 注入是一个过时的漏洞,但它今天仍然具有相关性,再次强调了采取防范措施以应对该漏洞的重要性。恶意代理和网络犯罪分子将始终尝试对 Web 应用进行 SQL 注入攻击,因此最好的做法是通过实践所有已知的防御机制来做好准备。
超越 SQL 注入
虽然 SQL 注入仍然是那些希望破坏应用程序的攻击者最常使用的攻击技术之一,但它绝对不是 Web 应用安全领域唯一的安全风险。
OWASP 十大 Web 应用安全风险
我们已经提到过 OWASP 十大 Web 应用安全风险,但让我们提供一个大致概述。它包含了最相关的 Web 应用安全风险,并按风险级别排序。此列表不定期更新。最近的一版是 2017 年的,但 2020 版正在制作中。
下面是完整的 OWASP 十大 Web 应用安全风险,并附有简要说明:
-
注入攻击:注入攻击是指插入不可信的数据,使其被解释为查询的一部分或更一般的命令。这包括 SQL 注入、NoSQL 注入和操作系统命令注入。
-
身份验证失效:身份验证和用户会话管理实施不当,导致攻击者通过获得比预期更高的权限或窃取用户身份来破坏应用程序。
-
敏感数据暴露:应用程序可能泄露敏感数据,如财务、医疗和个人信息,例如通过错误信息或可访问的资源。这使得这些数据容易遭受欺诈、身份盗窃或其他犯罪行为。
-
XML 外部实体攻击(XXE):如果 XML 文档中的任意外部实体引用被评估,则可能被用于泄露内部文件、敏感信息,并可能导致远程代码执行和拒绝服务攻击。
-
访问控制失效:用户权限的限制(即特定用户在应用程序中可以执行的操作)没有得到正确执行。这可能允许用户执行本应仅限管理员或更高权限的操作。
-
安全配置错误:用户所在的应用程序和/或系统在安全性方面没有正确配置。这包括不安全的默认配置、不完整或临时的配置以及不必要的错误信息。
-
跨站脚本攻击(XSS):XSS 允许攻击者通过将客户端脚本(通常是 JavaScript)插入到受害者的浏览器中执行,从而可能危及用户会话或诱使用户访问危险网站。
-
不安全的反序列化:输入未正确处理,且可以被应用程序直接接受,从而可能导致远程代码执行或注入攻击。
-
使用具有已知漏洞的组件:具有已知漏洞的外部组件可能会使应用程序暴露于各种攻击,具体取决于漏洞类型。这些组件需要及时应用最新的安全更新。
-
日志记录和监控不足:日志记录和监控不足意味着目标系统和应用程序没有正确地追踪可能的恶意行为,从而阻碍了任何可能的调查。
如您所见,SQL 注入虽然是最重要的漏洞类型之一,但只是冰山一角。这个列表只是为了向您展示 Web 应用程序可能在多少种方式下受到安全弱点的攻击。
在这样的背景下,安全专业人员在帮助保护应用程序和服务安全方面的作用显而易见。每当发现安全问题时,最好尽快采取措施进行缓解或解决。此外,通过阅读前十名的风险,您可能注意到某些风险之间是相互关联的。例如,不安全的反序列化可能会导致诸如注入等问题,这是由于对用户输入的不安全处理,而 XSS 可能是因为缺少输入验证,进而导致 SQL 注入。XSS 本身实际上是一种注入(在客户端脚本代码如 JavaScript 的情况下)。由于这些风险的相互关联,并且根据它们被检查的背景,安全专业人员主导的适当分析可以帮助优化缓解这些风险的过程。在此,他们选择优先处理最关键的问题,以最小化可能造成的影响。
OWASP Web 应用程序安全风险十大榜单进一步确认了 SQL 注入是 Web 应用程序中最关键的安全问题之一。这再次指向本书的任务:处理 SQL 注入问题,它是安全风险中最突出的风险之一。SQL 注入作为 Web 应用程序安全的理想切入点,带领我们进入更为复杂和全面的 Web 应用程序安全世界,而 Web 应用程序安全本身是计算机安全这一大分支的一部分,通常被称为信息安全或网络安全。
进一步探索信息安全
更深入地探索应用程序安全无疑是一个激动人心的道路,因为通过本书的实践部分所设立的测试环境,我们可以不断遇到新的挑战,并将直觉和技术技能付诸实践。这可以让你像某种侦探或医生一样,尝试做出正确的诊断。这对于有一些应用开发经验,想从安全角度审视已知问题的人尤其有益。
通常,安全专业人员可能需要在基础设施层面进行更多的操作。这种方法类似于 Web 应用程序安全,在这种情况下,您可以在目标系统上找到漏洞,这次是在系统级别。它可能需要精确了解操作系统如何工作,甚至了解在服务器和工作站上运行的协议和服务。由于需要直接在系统级别进行交互,而没有机会使用像 Web 应用程序安全评估中那样的用户友好界面,利用漏洞也可能变得相当具有挑战性。
网络安全的终极目标是为计算机和企业系统提供额外的安全层。这也可能导致采用针对性的安全解决方案,这些解决方案可以帮助组织和个人增强他们的安全态势。通过这种方式,安全专家可以根据初始背景建议最佳的安全解决方案。
无论如何,信息安全如今是最为讨论的话题之一。近年来,全球各国政府和超国家组织通过多项立法努力开始规范信息安全的各个方面。在考虑到网络战争的概念时,这一点尤为重要,因为即使是国家支持的网络攻击也可能针对全球的组织和实体进行。确保安全问题得到妥善处理至关重要,尤其是在考虑到组织乃至国家在面对计算机安全相关风险时,若未做好充分准备可能会面临的后果。
每天,信息安全总会以某种方式成为世界各地报纸和新闻的头条:这只是信息技术(IT)已成为我们社会中心的一个症状。因此,信息技术的安全变得极为重要,因为保护 IT 就是保护现实世界。随着时间的推移,这一说法变得愈发合理,因为技术已经与我们的生活紧密相连,渗透到我们日常生活的方方面面。
即使您对信息安全没有特别的兴趣,我们也建议您意识到它的重要性,因为它将始终贯穿我们的生活,特别是在未来。我们希望本书能够激发您进一步探索信息安全的话题。毕竟,您已经亲自了解了 SQL 注入的潜在后果。
总结
现在,我们来到了这段旅程的终点。在处理过我们所面对的所有主题之后,这一次以更加简洁的方式呈现,您掌握了关于信息安全的一些知识,并且看到即使是 SQL 注入漏洞(希望您已经掌握了这一主题),也能在现实世界中发挥重要作用。
既然您已经读完了本书,您可以随意探索信息安全的相关话题,或者继续在受控环境中进行实践。我们的希望是这段经历激发了您的好奇心,促使您更深入地了解安全话题。
感谢您阅读本书,并希望您在这个过程中也感到愉快。您可以随意使用您的模拟环境来测试 SQL 注入漏洞。您甚至可以使用 OWASP BWA 项目中的应用程序,了解其他安全问题。我们建议您首先探索 Mutillidae II 提供的所有内容,按照所有建议和指南进行操作。这将让您初步了解主要的 Web 应用程序安全风险。
现在你已经掌握了 SQL 注入,我们希望你能将所学应用于善事,而不是伤害任何人,尤其是考虑到你的行为可能带来的后果。
你知道他们怎么说的:伟大的力量伴随着伟大的责任。现在你已经掌握了 SQL 注入,要记得这一点。
问题
-
安全漏洞,包括 SQL 注入,通常是由什么引起的?
-
安全专业人员在漏洞测试中的工作是什么?
-
描述本书中我们识别的安全评估的三个主要阶段,不包括防御机制的实施。
-
你认为 SQL 注入作为一种老旧的漏洞,已经不再是一个真正的问题了吗?
-
SQL 注入在 OWASP 前 10 大 Web 应用程序安全风险列表中的排名是什么?
第五章:评估
第一章
-
数据库是一种以永久的方式存储数据的方式,通常以结构化和可访问的方式存储,以便其内容可以轻松地进行查询。
-
关系型数据库是一种使用表格来存储数据的数据库。这些表格建模对象及其之间的关系,正如名字所示。
-
结构化查询语言(SQL) 是一种用于与关系型数据库交互的语言,它依赖于通过简单语法构建查询。
-
一些基于 SQL 的系统示例包括 MySQL、SQLite、Microsoft SQL Server 和 Oracle 数据库。
-
SELECT
是最常见的 SQL 语句,它负责查询数据库并根据指定的要求返回数据。 -
SQL 注入是一种通过输入数据插入任意 SQL 命令的攻击。这可能导致对目标应用程序正在使用的数据库进行本应受限的操作。
第二章
-
SQL 注入可以通过使用特定字符触发,这些字符在 SQL 语法中对应特定的功能,例如字符串定界符,用于在不打算终止输入字符串的地方终止输入字符串。这样可以在后面插入 SQL 代码,以及注释字符,使系统忽略查询的某些部分。
-
攻击者可以通过一个网页表单插入任意查询,返回可能相关的信息。他们可以查询默认表来查看数据库结构,方法是将结果附加到现有查询结果中,使用
UNION
,并通过注释掉输入后要使用的查询部分来绕过验证。 -
攻击者可以通过两种方式绕过用户认证:他们可以从先前的注入攻击中获取密码,或者通过插入一个始终为真的语句来欺骗应用程序,从而导致登录成功。
-
盲 SQL 注入是一种不依赖数据库输出的 SQL 注入技术。它可以依赖布尔表达式,前提是应用程序在结果为真或假时表现不同,或者如果应用程序在输出上没有任何差异,它可以引入时间延迟。
-
由于在结果为真或假的情况下,输出没有显著差异,我们能做的就是依赖基于时间的 SQL 注入。
-
几乎任何依赖查询的数据库系统,如果输入未经过消毒或验证,都可能容易受到注入攻击。
第三章
-
虚拟化软件是一种完全模拟系统的特殊软件。我们使用它来确保我们的测试不涉及外部第三方,这意味着我们的一切都在一个受控环境中进行。
-
Kali Linux 是一个特殊的 Linux 发行版,包含一套为安全专业人员准备的软件。我们需要它来展示针对 Web 应用程序的自动化 SQL 注入攻击。
-
OWASP BWA 项目是一个以虚拟机形式存在的集合,包含故意设计有漏洞的 web 应用程序。我们通常使用它作为我们 web 应用攻击的目标。
-
我们模拟 web 服务,它代表了与传统 web 应用程序不同的接口,以及移动设备,在移动环境中显示漏洞。
-
绝对不行。只能对属于你的系统进行测试。未经明确和正式表达同意(即合同)的情况下,绝不可对属于第三方的系统进行测试。
第四章
-
二分查找在进行盲 SQL 注入时尤为有用,可以逐个字符猜测,例如使用 MySQL 的
ascii()
函数。 -
将 SQL 查询插入依赖外部语言/模块的函数参数中(如
ExtractValue()
函数)可能会在 SQL 错误中返回查询结果。 -
OWASP ZAP 提供了 Spider 工具,用于识别网页和可能存在漏洞的表单,另外还有 Scan 和 Fuzzer 工具,可以用于检查易受攻击的参数。
-
是的。sqlmap 具有一个密码破解模块,可以通过暴力破解从哈希值中提取密码。
-
SQL 注入可以针对任何类型的应用程序执行,包括 web 应用程序、web 服务和移动应用程序,如本章所示。
第五章
-
用户输入应始终视为潜在的恶意输入,因此必须始终进行清理和验证。
-
验证输入意味着在接受输入之前决定它是否有效。黑名单会阻止所有已知的无效输入,而白名单仅接受已知的有效输入。
-
参数化查询是一种构建查询语句的方法,它将查询字符串分解为参数。这些参数首先作为变量存储,然后插入到查询的主体中。
-
字符编码和转义对于转化可能会被 SQL 解析的有害字符非常有用,这些字符可能会导致 SQL 注入攻击。
-
Web 应用防火墙(WAF)的目的是在应用层过滤请求,并识别和防止危险的请求到达应用程序。
-
不。如果攻击者获得了加密数据,他们也可能获得加密密钥,从而使加密变得毫无意义。
第六章
-
安全漏洞通常是代码错误、配置错误或协议问题的结果。
-
安全专业人员在测试漏洞时,必须评估漏洞的可利用性,从而正确评估其影响和风险。
-
首先,发现漏洞。发现之后进行测试,以评估其风险。最后,基于已识别的风险,制定修复计划。
-
SQL 注入,尽管是一个老旧的漏洞,依然在实际环境中存在,并且在今天仍然是一个问题。
-
SQL 注入作为一种注入问题,位居 OWASP Web 应用安全风险十大漏洞之首。
第六章:其他您可能感兴趣的书籍
如果您喜欢这本书,您可能还会对 Packt 出版的以下其他书籍感兴趣:
Linux 安全与强化实战 - 第二版
Donald A. Tevault
ISBN: 978-1-83898-177-8
-
创建锁定的用户账户,并设置强密码
-
配置防火墙,包括 iptables、UFW、nftables 和 firewalld
-
使用不同的加密技术保护您的数据
-
强化安全外壳服务,防止安全漏洞
-
使用强制访问控制保护系统免受漏洞攻击
-
强化内核参数并设置内核级审计系统
Mastering Windows Security and Hardening
Mark Dunkerley, Matt Tumbarello
ISBN: 978-1-83921-641-1
-
理解基准化并学习构建基准的最佳实践
-
掌握基于 Windows 系统的身份管理和访问管理
-
深入了解基于 Windows 系统的设备管理和远程管理
-
探索硬化 Windows 服务器并保护客户端的安全性
-
审计、评估和测试,确保控制措施成功应用并执行
-
监控并报告活动,以掌握漏洞情况
留下评论 - 告诉其他读者您的想法
请通过在购买该书的网站上留下评论,与他人分享您对本书的想法。如果您是从亚马逊购买的书籍,请在本书的亚马逊页面上留下真实的评价。这对其他潜在读者非常重要,他们可以通过您的公正意见做出购买决定;我们也能了解客户对我们产品的看法;作者们也能看到您对他们与 Packt 合作创作的书籍的反馈。这仅需您几分钟时间,但对其他潜在客户、我们的作者以及 Packt 来说都非常宝贵。谢谢!