PHP表单CSRF Token校验实现及两token始终不匹配问题求助
问题原因排查
- 函数名大小写不匹配
你定义的生成函数名为RandomToken,但在config.php中存储session时写的是randomToken(),PHP对函数名大小写敏感,此处会调用失败,session中存储的实际是null或者错误值,自然无法匹配。
- 函数名大小写不匹配
- Session存储值和表单输出值不是同一个
你定义的$token是RandomToken() + 换行 + Salt() + 换行的拼接值,但$_SESSION['t']里直接存的是单次randomToken()的返回值,两个值完全不同,表单输出的是$token,和session内的值对比必然不一致。
- Session存储值和表单输出值不是同一个
- 表单页面未引入config.php
form-page.php中仅引入了../connection/cnt.php,没有引入config.php,表单里调用的$token属于未定义变量,输出的是空值或者PHP报错内容,提交后当然匹配失败。
- 表单页面未引入config.php
- 核心逻辑顺序错误
CSRF token的正确逻辑是:用户访问表单页时生成一次token,存入session同时输出到表单。提交校验时直接读取session中之前存的旧token和提交的值对比,你此前的旧版本代码甚至在提交校验时先重新生成token覆盖session的值,相当于拿新生成的token和用户提交的旧token比,永远不可能一致。
- 核心逻辑顺序错误
- 冗余的session_start调用
重复调用session_start()没有必要,PHP高版本虽然不会直接报错,但如果在有输出后调用容易触发「头信息已发送」警告,反而影响功能。
- 冗余的session_start调用
修复方案
1. 修正config.php代码
<?php ob_start(); // 全局只启动一次session即可 if (session_status() === PHP_SESSION_NONE) { session_start(); } function RandomToken($length = 32){ if(!isset($length) || intval($length) <= 8 ){ $length = 32; } if (function_exists('random_bytes')) { return bin2hex(random_bytes($length)); } if (function_exists('mcrypt_create_iv')) { return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)); } if (function_exists('openssl_random_pseudo_bytes')) { return bin2hex(openssl_random_pseudo_bytes($length)); } return ''; } // 仅在访问表单页、且当前session中没有token的时候才生成新token if (!isset($_SESSION['t'])) { $random = RandomToken(32); // 不需要多余的换行和盐,随机字符串本身已经足够安全 $_SESSION['t'] = $random; } // 统一用session里的token输出到表单,保证前后一致 $token = $_SESSION['t']; ?>
2. 修正form-page.php代码
<?php ob_start(); // 先引入config.php,自动处理session和token include 'config.php'; // 改成你自己的config.php实际路径 include '../connection/cnt.php'; if (isset($_POST['submit'])) { // 直接拿session里存的旧token对比 if (empty($_POST['csrf_token_p']) || $_SESSION['t'] !== $_POST['csrf_token_p']) { // 校验失败跳转 ?> <script type="text/javascript"> window.location = "p_tl.php?edit=N"; </script> <?php exit; } // 校验通过,销毁当前token防止重复提交(可选,根据业务需求决定) unset($_SESSION['t']); // 这里写你的业务逻辑 } ?> <form id="validate" action="p_tl.php" method="post" class="needs-validation" novalidate> <!-- 建议改成hidden类型,不需要显示给用户 --> <input type="hidden" name="csrf_token_p" value="<?php echo htmlspecialchars($token); ?>" class="form-control" id="validationCustom01" required> <input style="background-color: #9da1a4;color: #fff;" name="submit" value="modifica" class="btn btn-sm" type="submit"> </form>
内容的提问来源于stack exchange,提问作者user15775524
相关产品推荐
相关产品推荐

