PHP+MySQL登录系统CSRF令牌验证失败问题求助
问题分析与解决方案
核心问题定位
- 重复生成CSRF令牌:访问
index.php和login.php时分别生成令牌,导致登录提交的令牌与当前会话绑定的令牌不匹配 - 首页空令牌记录:
index.php的CSRF生成逻辑存在漏洞,未生成有效令牌或未关联会话ID - Invalid CSRF Token错误:令牌不匹配、会话关联失效、验证逻辑错误三者共同或单独导致
分步排查与修复
1. 统一CSRF令牌生成入口
移除index.php中的CSRF令牌生成代码,仅在**登录入口页面login.php**生成令牌,避免重复创建:
session_start(); // 仅当会话中无有效令牌时生成新令牌 if (!isset($_SESSION['csrf_token'])) { $csrf_token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $csrf_token; // 存入数据库时绑定当前会话ID,避免空记录 $stmt = $pdo->prepare("INSERT INTO csrf_tokens (token, session_id, created_at) VALUES (?, ?, NOW())"); $stmt->execute([$csrf_token, session_id()]); }
2. 修复空令牌记录问题
- 彻底删除
index.php中所有CSRF令牌生成逻辑,将令牌操作统一交由login.php处理 - 检查
csrf_tokens表结构,确保session_id字段长度足够存储PHP会话ID(建议设为50字符)
3. 修正CSRF验证逻辑
登录验证时需同时完成三重校验:
session_start(); // 校验请求中的令牌是否存在且与会话令牌一致 if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { die("Invalid CSRF Token"); } // 校验数据库中令牌是否与当前会话绑定(防止会话劫持后的令牌复用) $stmt = $pdo->prepare("SELECT * FROM csrf_tokens WHERE token = ? AND session_id = ?"); $stmt->execute([$_POST['csrf_token'], session_id()]); if (!$stmt->fetch()) { die("Invalid CSRF Token"); } // 验证通过后,删除已使用的令牌避免重复利用 $stmt = $pdo->prepare("DELETE FROM csrf_tokens WHERE token = ?"); $stmt->execute([$_POST['csrf_token']]);
4. 兼容“记住我”会话配置
确保会话有效期设置正确,且不会重置会话ID导致令牌关联失效:
session_set_cookie_params([ 'lifetime' => 30*24*60*60, // 30天有效期 'path' => '/', 'secure' => true, // HTTPS环境下启用 'httponly' => true, 'samesite' => 'Strict' ]); session_start();
5. 调试方法
在login.php页面添加调试输出,确认令牌与会话的一致性:
echo "当前会话ID: " . session_id() . "<br>"; echo "当前CSRF令牌: " . $_SESSION['csrf_token'] . "<br>";
内容的提问来源于stack exchange,提问作者Youssef Ahmed
相关产品推荐
相关产品推荐

