零基础玩转 bWAPP 靶场(四):HTML 注入(当前 URL 反射) 📅 2026/7/17 21:17:28 摘要本文是 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”。如果网站没有做任何防护你搜索h1hello/h1页面显示的就是一个加粗放大的“hello”——HTML 标签被成功解析了2.2 HTML 注入与 XSS 的区别很多初学者会混淆 HTML 注入和 XSS跨站脚本攻击。这里用最简单的方式说清楚HTML 注入注入的是 HTML 标签如h1、a、img等主要用于修改页面内容和布局。XSS跨站脚本攻击注入的是 JavaScript 脚本代码如scriptalert(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?scriptalert(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?h1Hacked!/h1 HTTP/1.1或者更简洁的方式GET /htmli_current_url.php/h1Hacked!/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 级别一样的方法修改 URIGET /bWAPP/htmli_current_url.php?h1Hacked!/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 抓包修改 URIGET /bWAPP/htmli_current_url.php?h1Hacked!/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 中注入什么恶意代码和都会被转换成lt;和gt;浏览器会将其显示为普通文本而不会解析为 HTML 标签。6.3 如何防御High 级别的正确做法High 级别的防御方案是正确且有效的服务端获取数据不依赖客户端所有数据处理在服务端完成。输出编码使用htmlspecialchars()对输出内容进行 HTML 实体编码。统一字符编码指定 UTF-8 编码避免编码绕过问题。这是防御 HTML 注入和 XSS 攻击的核心原则对输出进行编码Output Encoding。七、三种安全级别对比总结级别获取方式处理方式是否安全漏洞状态LowPHP$_SERVER[REQUEST_URI]直接输出无任何过滤❌ 不安全存在漏洞MediumJavaScriptdocument.URL浏览器自动 URL 编码⚠️ 部分有效依赖浏览器存在绕过可能HighPHP$_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?h1Hacked!/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 参数、表单输入等位置尝试注入h1test/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页面前必须执行编码转义处理。重要声明本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。你的关注是我坚持原创和细节共享的力量来源谢谢大家。