零基础玩转 bWAPP 靶场(四):HTML 注入(当前 URL 反射)
摘要:本文是 bWAPP 靶场系列的第四篇,聚焦于HTML Injection - Reflected (Current URL)漏洞。文章从零基础角度出发,详细讲解什么是 HTML 注入、它与 XSS 的区别与联系、该漏洞的产生原因与危害,然后按照 Low、Medium、High 三个安全级别逐一进行源码分析、通关演示和防御方案讲解。
一、前言
在前三篇文章中,我们分别介绍了 bWAPP 的环境搭建、HTML Injection - Reflected (GET) 和 HTML Injection - Reflected (POST) 两个漏洞。今天,我们将继续深入,学习一个非常特殊且有趣的漏洞类型——HTML Injection - Reflected (Current URL),即基于当前 URL 的反射型 HTML 注入。
为什么要单独把“Current URL”拿出来讲?因为它的攻击方式和前两篇完全不同——前两篇是通过表单参数(GET 或 POST)注入,而这一关的注入点不在任何表单输入框里,而在浏览器地址栏的 URL 本身!这种特殊的注入方式,让很多初学者摸不着头脑。本文将带你一步步揭开它的神秘面纱。
二、HTML 注入概述
2.1 什么是 HTML 注入?
HTML 注入(HTML Injection),中文全称是“超文本标记语言注入”。简单来说,当 Web 应用程序对用户输入的数据没有进行充分的过滤和转义处理时,攻击者可以提交包含 HTML 标签的恶意数据。如果服务器将这些数据当作正常的 HTML 内容直接输出到页面中,那么浏览器就会解析并执行这些标签。
举个例子:假如一个网站有一个搜索框,你搜索“hello”,页面显示“您搜索的关键词是:hello”。如果网站没有做任何防护,你搜索<h1>hello</h1>,页面显示的就是一个加粗放大的“hello”——HTML 标签被成功解析了!
2.2 HTML 注入与 XSS 的区别
很多初学者会混淆 HTML 注入和 XSS(跨站脚本攻击)。这里用最简单的方式说清楚:
HTML 注入:注入的是 HTML 标签(如
<h1>、<a>、<img>等),主要用于修改页面内容和布局。XSS(跨站脚本攻击):注入的是 JavaScript 脚本代码(如
<script>alert(1)</script>),可以执行任意恶意脚本,危害更大。
通俗理解:HTML 注入是“改页面样子”,XSS 是“在页面里跑恶意程序”。两者本质上都是对用户输入处理不当导致的,但 XSS 的危害级别更高。不过在实际攻防中,能注入 HTML 标签的地方,往往也能注入 XSS 脚本——因为<script>本身也是一个 HTML 标签。
2.3 为什么会产生 HTML 注入漏洞?
根本原因只有一句话:应用程序将用户输入的内容未经处理地直接输出到了 HTML 页面中。
具体来说,当发生以下情况时,就可能产生 HTML 注入漏洞:
缺乏输入验证:没有对用户提交的数据进行合法性检查。
缺乏输出编码:直接将用户数据拼接到 HTML 响应中,没有进行 HTML 实体编码。
信任了用户可控的数据源:比如 URL 参数、HTTP 请求头、Cookie 等。
2.4 HTML 注入的危害
HTML 注入的危害不容小觑:
网页内容篡改:攻击者可以修改页面显示的内容,插入虚假信息或恶意链接。
钓鱼攻击:在页面中嵌入伪造的登录表单,诱导用户输入账号密码。
配合 XSS 实施更严重的攻击:通过注入
<script>标签,可以窃取 Cookie、劫持会话、进行键盘记录等。社会工程学攻击:利用篡改后的页面欺骗用户,实施诈骗。
三、HTML Injection - Reflected (Current URL) 漏洞详解
3.1 什么是“Current URL”反射型注入?
在讲解具体关卡之前,先理解这个漏洞的核心机制。
“Current URL”指的是当前页面的完整 URL 地址。bWAPP 的这个漏洞页面会将当前页面的 URL 地址直接显示在页面上,就像这样:
Your current URL: http://localhost/bWAPP/htmli_current_url.php
问题来了:如果服务器直接把 URL 中的内容原封不动地拼接到 HTML 页面中,那么攻击者就可以在 URL 后面添加恶意 HTML 代码,让浏览器去解析执行。
3.2 这个漏洞的特殊之处
与前面两篇讲的 GET/POST 型 HTML 注入相比,Current URL 类型有几个特殊之处:
注入点不在表单:没有输入框让你填东西,注入点就是浏览器地址栏的 URL。
浏览器会 URL 编码:直接在地址栏输入
<script>,浏览器会自动编码成%3Cscript%3E,可能导致注入失败。浏览器差异:这个漏洞在IE 浏览器上更容易复现,Chrome 和 Firefox 由于内置了 XSS 过滤器,可能不生效。
需要抓包工具:很多时候需要借助 Burp Suite 等抓包工具来绕过浏览器的自动编码。
3.3 漏洞产生的历史背景
这类漏洞在 Web 应用发展的早期非常普遍。那时候开发者对安全意识的重视程度不够,常常为了方便,直接将$_SERVER["REQUEST_URI"]等服务器变量输出到页面中。
随着浏览器安全功能的不断增强(如 Chrome 的 XSS Auditor、Firefox 的 XSS 过滤器),直接在地址栏注入变得越来越困难。但这并不意味着漏洞不存在了——攻击者仍然可以通过抓包工具修改请求,或者利用旧版本浏览器来实施攻击。这也提醒我们:安全不能依赖浏览器来防护,服务端必须做好自己的本职工作。
四、Low 安全级别
4.1 漏洞复现(通关)
步骤一:进入漏洞页面
在 bWAPP 主界面选择HTML Injection - Reflected (Current URL),将安全级别设置为Low,然后点击“Hack”按钮进入漏洞页面。
正常情况下,页面会显示类似这样的内容:
Your current URL: http://localhost/bWAPP/htmli_current_url.php
步骤二:尝试直接地址栏注入
很多初学者会直接在浏览器地址栏后面添加参数,比如:
http://localhost/bWAPP/htmli_current_url.php?<script>alert(1)</script>但是!你会发现不成功。为什么呢?
因为浏览器会自动对 URL 中的特殊字符进行编码。<会被编码成%3C,>会被编码成%3E。服务器收到的是编码后的内容,输出到页面后显示的就是%3Cscript%3Ealert(1)%3C/script%3E这样的纯文本,而不是可执行的 HTML 标签。
步骤三:使用 Burp Suite 抓包注入(正确方法)
正确的做法是使用 Burp Suite 等抓包工具:
打开 Burp Suite,开启代理拦截。
在浏览器中正常访问该页面,让 Burp 拦截到请求。
在 Burp 中修改请求的URI 路径,在末尾添加恶意载荷:
GET /htmli_current_url.php?<h1>Hacked!</h1> HTTP/1.1或者更简洁的方式:
GET /htmli_current_url.php/<h1>Hacked!</h1> HTTP/1.1转发请求,查看响应。
步骤四:验证注入成功
如果注入成功,页面上的“Your current URL:”后面就会显示一个加粗放大的“Hacked!”。
4.2 源码分析
打开 bWAPP 源码目录下的htmli_current_url.php文件,查看 Low 级别的核心代码:
case "0": // Low安全等级 // 注释掉了解码函数,不做任何URL解码处理 // $url = "http://" . $_SERVER["HTTP_HOST"] . urldecode($_SERVER["REQUEST_URI"]); $url = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; break; // 页面直接拼接输出完整URL,无任何转义 echo "Your current URL: " . $url;代码解析:
$_SERVER["REQUEST_URI"]存储浏览器发送的原始未解码 URI 字符串,所有%xxURL 编码字符完整保留;代码拼接域名与 URI 得到完整访问链接,直接
echo输出到 HTML 页面;全程无 URL 解码、无 HTML 实体转义、无黑名单过滤;
漏洞本质:浏览器自动对页面内 URL 文本执行 URL 解码,还原出
<h1>等 HTML 标签并渲染,形成 HTML 注入。
这就是漏洞产生的根本原因:用户可控的数据(URL)被直接输出到了 HTML 中。
4.3 如何防御(Low 级别的正确做法)
对于 Low 级别暴露的问题,最基础的防御措施是:永远不要直接将用户输入的数据输出到 HTML 中。
即使是最简单的防御,也应该做到:
// 至少应该对输出进行 HTML 实体编码 $url = htmlspecialchars($_SERVER["REQUEST_URI"], ENT_QUOTES, 'UTF-8'); echo "Your current URL: " . $url;htmlspecialchars()函数会将<、>、"、'、&等特殊字符转换为 HTML 实体,这样浏览器就会把它们当作普通文本显示,而不会解析为 HTML 标签。
五、Medium 安全级别
5.1 漏洞复现(通关)
将安全级别切换为Medium,再次访问该页面。
尝试一:直接地址栏注入
和 Low 级别一样,直接在地址栏添加恶意代码——不成功。浏览器会自动编码。
尝试二:Burp Suite 抓包注入
用 Burp Suite 抓包,尝试和 Low 级别一样的方法修改 URI:
GET /bWAPP/htmli_current_url.php?<h1>Hacked!</h1> HTTP/1.1你会发现——也不成功!
为什么会失败呢?我们来看看源码。
5.2 源码分析
Medium 级别的核心代码:
case "1": // Medium 级别 ?> <script> document.write(document.URL); </script> <?php break;代码解析:
Medium 级别的实现方式发生了根本性的变化:
不再使用 PHP 的
$_SERVER["REQUEST_URI"]在服务端获取 URL。而是使用JavaScript 的
document.write(document.URL)在浏览器本地(客户端)获取并输出当前 URL。
这意味着什么?
URL 的获取和输出全部在浏览器端完成,没有经过服务端的处理。
document.URL返回的是当前页面的完整 URL,但浏览器在将 URL 传递给 JavaScript 之前,已经对特殊字符进行了 URL 编码。因此,即使你在 URL 中写了
<h1>,到了document.URL这里已经变成了%3Ch1%3E。
简单说:浏览器自己就把恶意代码给“消毒”了——这不是服务端的主动防御,而是浏览器的“被动”行为。
5.3 如何防御(Medium 级别的评价)
Medium 级别的实现方式并不是一个有效的安全防御方案,原因如下:
依赖客户端行为:安全措施不能依赖浏览器(客户端)来实现,因为攻击者可以控制客户端环境。
治标不治本:虽然在这个特定场景下因为 URL 编码而“碰巧”阻止了注入,但这并不是设计上的安全防护。
不同浏览器表现不同:某些旧版本浏览器或特殊配置下,可能仍然存在风险。
正确的做法:无论使用什么方式获取数据,在输出到 HTML 之前都应该在服务端进行严格的过滤和编码。
六、High 安全级别
6.1 漏洞复现(通关)
将安全级别切换为High,再次尝试注入。
用 Burp Suite 抓包修改 URI:
GET /bWAPP/htmli_current_url.php?<h1>Hacked!</h1> HTTP/1.1结果:不成功。
6.2 源码分析
High 级别的核心代码:
case "2": // High 级别 $url = "http://" . $_SERVER["HTTP_HOST"] . xss_check_3($_SERVER["REQUEST_URI"]); echo "Your current URL: " . $url; break;这里调用了一个名为xss_check_3()的函数来处理 URL。我们来看看这个函数的实现:
function xss_check_3($data) { return htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); }代码解析:
High 级别回到了服务端获取 URL(使用
$_SERVER["REQUEST_URI"])。但在输出之前,调用了
xss_check_3()函数。xss_check_3()函数内部使用了htmlspecialchars()进行 HTML 实体编码。htmlspecialchars()会将<、>、"、'、&等特殊字符转换为对应的 HTML 实体。
这意味着:无论你在 URL 中注入什么恶意代码,<和>都会被转换成<和>,浏览器会将其显示为普通文本,而不会解析为 HTML 标签。
6.3 如何防御(High 级别的正确做法)
High 级别的防御方案是正确且有效的:
服务端获取数据:不依赖客户端,所有数据处理在服务端完成。
输出编码:使用
htmlspecialchars()对输出内容进行 HTML 实体编码。统一字符编码:指定 UTF-8 编码,避免编码绕过问题。
这是防御 HTML 注入和 XSS 攻击的核心原则:对输出进行编码(Output Encoding)。
七、三种安全级别对比总结
| 级别 | 获取方式 | 处理方式 | 是否安全 | 漏洞状态 |
|---|---|---|---|---|
| Low | PHP$_SERVER["REQUEST_URI"] | 直接输出,无任何过滤 | ❌ 不安全 | 存在漏洞 |
| Medium | JavaScriptdocument.URL | 浏览器自动 URL 编码 | ⚠️ 部分有效(依赖浏览器) | 存在绕过可能 |
| High | PHP$_SERVER["REQUEST_URI"] | htmlspecialchars()编码输出 | ✅ 安全 | 漏洞已修复 |
八、为什么 Low 级别必须用 Burp Suite?
这是一个初学者经常问的问题:为什么 Low 级别也不能直接在地址栏注入?
答案有三层:
第一层:浏览器的自动 URL 编码
当你在浏览器地址栏输入特殊字符(如<、>、#、%等)时,浏览器会自动将它们编码为%开头的十六进制形式。
<→%3C>→%3E#→%23"→%22
服务器收到的是编码后的字符串,原封不动地输出到页面,显示的就是%3Ch1%3E这样的文本。
第二层:HTTP 请求的构成
当你访问http://localhost/bWAPP/htmli_current_url.php?<h1>时,浏览器实际发出的 HTTP 请求是:
GET /bWAPP/htmli_current_url.php?%3Ch1%3E HTTP/1.1 Host: localhost看到了吗?<script>已经变成了%3Ch1t%3E。
第三层:Burp Suite 的作用
Burp Suite 作为中间人代理,可以在请求发出之前拦截并修改它。你可以绕过浏览器的自动编码,直接发送原始的特殊字符:
GET /bWAPP/htmli_current_url.php?<h1>Hacked!</h1> HTTP/1.1这样服务器收到的就是原始的<h1>,如果服务端没有过滤,就会被成功注入。
九、实战防御方案总结
9.1 开发人员必知的防御措施
方案一:输出编码(最推荐)
使用htmlspecialchars()对输出内容进行 HTML 实体编码:
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');这是防御 HTML 注入最有效、最标准的方法。
方案二:输入验证(白名单)
对用户输入进行严格的格式验证,只允许符合预期格式的数据通过:
// 例如:只允许字母和数字 if (!preg_match('/^[a-zA-Z0-9]+$/', $user_input)) { die("Invalid input"); }方案三:使用安全框架和模板引擎
现代 Web 框架(如 Laravel、Django、Spring)和模板引擎(如 Twig、Jinja2)默认会对输出进行自动编码,大大降低了注入风险。
方案四:内容安全策略(CSP)
通过设置 HTTP 响应头Content-Security-Policy,限制浏览器只能执行来自可信源的脚本。
9.2 安全测试人员必知的检测方法
手工测试:在 URL 参数、表单输入等位置尝试注入
<h1>test</h1>等简单 HTML 标签,观察是否被解析。抓包测试:使用 Burp Suite 等工具绕过浏览器编码,测试服务端是否存在过滤。
编码绕过测试:尝试 URL 编码、双编码等绕过方式。
浏览器兼容性测试:在不同浏览器(尤其是旧版本 IE)上测试。
十、总结
通过本篇文章学习,我们全面掌握了bWAPP中HTML Injection - Reflected (Current URL)反射型当前URL HTML注入漏洞相关知识:HTML注入的本质是可控用户数据未经任何处理直接输出至页面,恶意HTML标签会被浏览器正常解析,而XSS属于HTML注入的进阶利用,可注入并执行JavaScript脚本;该漏洞区别于常规表单注入,注入载体为URL地址,通常需借助抓包工具绕过浏览器自动编码限制。靶场分三级防护,Low级别直接输出原始URL无任何防护,存在明显注入漏洞;Medium级别通过前端JavaScript获取页面URL展示,仅转移输出逻辑,防护方案存在缺陷、无法彻底阻断注入风险;High级别采用htmlspecialchars()对输出内容做HTML实体编码,能有效防御该类漏洞。整体防御核心原则为不可信任任意用户可控输入,数据输出到HTML页面前必须执行编码转义处理。
重要声明:本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。
