You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP:同一页面多表单使用CSRF Token重复生成问题解决方案

解决同一页面多表单共用CSRF Token的问题

问题出在你的csrf_token_tag()函数每次调用都会执行create_csrf_token(),而这个函数每次都会生成新的Token并覆盖session中的值。这样第一个表单的Token是第一次生成的,第二个表单用的是第二次生成的,而session里只保留了最后一次的Token,提交第一个表单时自然会验证失败。

修改后的完整代码

<?php
session_start(); // 必须在所有session操作前开启session

// 生成随机CSRF Token
function csrf_token() {
    return bin2hex(random_bytes(35));
}

// 获取或创建有效的CSRF Token(有效期设为1小时,可按需调整)
function get_csrf_token() {
    $expiration = 3600; // Token有效期,单位:秒
    // 检查session中是否存在有效Token
    if (isset($_SESSION['csrf_token']) && isset($_SESSION['csrf_token_time']) 
        && (time() - $_SESSION['csrf_token_time']) < $expiration) {
        return $_SESSION['csrf_token'];
    }
    // 没有有效Token时,生成新的并存入session
    $token = csrf_token();
    $_SESSION['csrf_token'] = $token;
    $_SESSION['csrf_token_time'] = time();
    return $token;
}

// 生成CSRF Token的隐藏输入标签
function csrf_token_tag() {
    $token = get_csrf_token(); // 复用已有Token,而非每次生成新的
    return '<input type="hidden" name="csrf_token" value="' . $token . '">';
}

// 生成一次Token标签,或者直接在表单里调用csrf_token_tag()都可以
$csrf_token = csrf_token_tag();
?>

<form action="" method="post">
    <!-- 表单1内容 -->
    <?= $csrf_token; ?>
    <button type="submit">提交表单1</button>
</form>

<form action="" method="post">
    <!-- 表单2内容 -->
    <?= csrf_token_tag(); ?> <!-- 这里直接调用函数也会返回同一个Token -->
    <button type="submit">提交表单2</button>
</form>

表单提交后的验证逻辑

不管提交哪个表单,验证逻辑都是一样的,确保Token匹配且未过期:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 检查Token是否存在
    if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token'])) {
        die("CSRF验证失败");
    }
    
    $expiration = 3600;
    // 验证Token是否匹配且未过期
    if ($_POST['csrf_token'] !== $_SESSION['csrf_token'] 
        || (time() - $_SESSION['csrf_token_time']) >= $expiration) {
        die("CSRF验证失败");
    }

    // 验证通过,处理你的表单业务逻辑
    echo "表单提交成功!";
}

关键说明

  1. get_csrf_token()函数:核心逻辑是优先复用session中已有的有效Token,只有在Token不存在或过期时才生成新的,这样同一页面的所有表单都会使用同一个Token。
  2. session_start():必须在所有session操作之前调用,否则无法读取或写入session中的Token。
  3. 有效期设置:你可以根据需求调整$expiration的值,比如设置为1800秒(30分钟),平衡安全性和用户体验。

内容的提问来源于stack exchange,提问作者yikemop651

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:52:51