跨站脚本攻击(XSS)是渗透测试中最常见且危害隐蔽的Web漏洞类型之一。它的核心问题在于应用程序未能对用户输入进行充分过滤或转义,导致恶意脚本被浏览器当作合法内容执行。在真实的渗透项目中,检测XSS不能只靠扫描器,更需要理解页面渲染机制和数据处理流程,才能发现那些自动化工具容易遗漏的逻辑型缺陷。

理解XSS类型与触发原理
在着手检测前,必须先厘清三种主流XSS的差异。反射型XSS通常出现在搜索框、跳转参数中,恶意脚本作为请求一部分发送,服务器将其原样返回到响应页面,浏览器立即解析执行。这类漏洞利用需要诱导用户点击特制链接,因此在渗透测试时要重点遍历所有带参数的URL并观察回显位置。
存储型XSS则更加危险,攻击者提交的内容被保存到数据库,之后任意用户访问相关页面都会加载该脚本。论坛评论、用户昵称、私信功能都是高发区。检测时需要向所有可能持久化输入的接口注入探针,并切换不同账号或退出登录后重新访问,确认脚本是否留存。
DOM型XSS与前两者不同,漏洞位于前端JavaScript代码内部。例如脚本读取location.hash后直接赋值给innerHTML,未经过滤就改变文档结构。这类问题不会在服务器响应中看到 payload,必须借助浏览器开发者工具在客户端动态调试才能发现。渗透测试中建议使用控制台监视DOM突变,并修改URL片段测试各种输入。
手工检测载荷与绕过过滤手法
基础的检测载荷可以从简单的标签开始,例如向输入框提交<script>alert(1)</script>,观察页面是否弹窗或源码中是否未转义该片段。但现代应用多部署了基础过滤器,此时需要变换大小写、插入空格或利用事件处理器,如<img src=x onerror=alert(1)>。由于尖括号可能被拦截,还可尝试不使用标签的向量,例如将payload放在onfocus属性中并通过Tab键触发。
面对黑名单过滤,渗透测试人员常采用嵌套绕过。比如某些系统只过滤一次script,可构造<scr<script>ipt>使过滤器移除中间标签后反而形成合法标签。另外,利用JavaScript伪协议写入href属性也是一种思路:<a href=javascript:alert(1)>click</a>。在检测富文本字段时,应当用白名单思维反向测试,提交包含数十种标签与属性的组合,记录哪些被剥离、哪些存活。
编码绕过也是必备技能。当输入点处于HTML属性值时,可以尝试将字符转为实体或十六进制,例如alert写成alert。有些前端框架会先解码再渲染,如果服务端只校验原始编码形态就会漏判。下面是一段用于快速生成多种编码变体的Python脚本,可在本地测试时辅助构造请求:
# 生成常见XSS绕过编码示例
def to_entity(s):
return ''.join('&#{};'.format(ord(c)) for c in s)
payload = '<script>alert(1)</script>'
print(to_entity(payload))
# 输出: <script>alert(1)</script>
结合工具与代码审计提升覆盖率
纯手工检测效率有限,渗透测试应搭配专用工具。Burp Suite的Scanner能主动爬取并插入探针,其Collaborator模块还可发现盲打型XSS,即脚本触发后向外部服务器发起请求从而证明执行。自写爬虫配合正则表达式提取表单字段,也能批量提交检测向量。需要注意的是,扫描器报出的“疑似”必须人工复验,避免误报影响报告质量。
代码审计是从根源发现XSS的方式。在拿到目标源码时,全局搜索输出函数如echo、print、innerHTML赋值点,检查是否调用了转义方法。例如PHP中未使用htmlspecialchars直接输出$_GET参数即为隐患。前端框架如Vue、React默认对插值做转义,但若开发者使用v-html或dangerouslySetInnerHTML则重新引入风险。审计时应建立输入到输出的数据流图谱,标注每个穿越点是否经过净化。
最后,在撰写渗透测试报告时,不仅要列出URL和payload,还需说明漏洞类型、触发条件与修复建议。推荐修复方案包括:服务端对所有输出上下文做对应编码,客户端避免动态插入未信任内容,以及设置Content-Security-Policy响应头限制脚本来源。只有将检测手法与防御认知结合,才能体现渗透测试的真正价值。