本文目录导读:

在 PHP 项目中添加 CSRF 令牌防护,通常遵循以下标准流程:
生成 CSRF 令牌(通常在 Session 中)
首先要有一个安全的令牌生成函数:
// 初始化 session
session_start();
// 生成 CSRF 令牌
function generateCsrfToken() {
// session 中没有令牌,则生成一个新的
if (empty($_SESSION['csrf_token'])) {
// 使用 random_bytes 生成安全的随机值
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 获取当前令牌
$csrfToken = generateCsrfToken();
在表单中添加隐藏字段
将令牌嵌入到所有需要保护的 POST/PUT/DELETE 表单中:
<form method="POST" action="/submit.php">
<!-- 其他表单字段 -->
<input type="text" name="username">
<input type="password" name="password">
<!-- CSRF 令牌隐藏字段 -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
<button type="submit">提交</button>
</form>
对于 AJAX 请求,可以在 HTML 的 meta 标签中存储令牌:
<meta name="csrf-token" content="<?php echo htmlspecialchars($csrfToken); ?>">
然后在 JavaScript 中读取并发送:
// 使用 Fetch API
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch('/api/endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({/* 请求体 */})
});
// 或者在 AJAX 中
$.ajax({
url: '/api/endpoint',
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken
},
data: { /* 数据 */ }
});
验证 CSRF 令牌(在服务端处理请求时)
在每个需要保护的后端处理入口进行验证:
// 验证 CSRF 令牌的函数
function validateCsrfToken($token) {
// 检查 session 中是否存在令牌
if (!isset($_SESSION['csrf_token'])) {
return false;
}
// 使用 hash_equals 防止时序攻击
return hash_equals($_SESSION['csrf_token'], $token);
}
// 在 POST 请求处理中
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 获取提交的令牌
$submittedToken = $_POST['csrf_token'] ?? '';
// 验证令牌
if (!validateCsrfToken($submittedToken)) {
// 令牌无效,终止请求
http_response_code(403);
die('CSRF 令牌验证失败,请求被拒绝。');
}
// 令牌验证通过,继续处理请求
// ... 业务逻辑代码
}
可选:令牌轮换(提高安全性)
每次成功验证后可以生成新令牌(防止令牌被反复使用):
function rotateCsrfToken() {
// 生成新令牌并覆盖旧令牌
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
return $_SESSION['csrf_token'];
}
完整封装示例
创建一个 CSRF 防护类以便复用:
class CsrfProtection {
public static function init() {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
}
public static function getToken() {
self::init();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
public static function validateToken($token) {
self::init();
if (!isset($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_SESSION['csrf_token'], $token);
}
public static function generateTokenField() {
$token = self::getToken();
return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars($token) . '">';
}
}
使用方式:
// 页面模板中
<!DOCTYPE html>
<html>
<body>
<form method="POST" action="/process.php">
<?php echo CsrfProtection::generateTokenField(); ?>
<input type="text" name="data">
<input type="submit">
</form>
</body>
</html>
// 后端处理中
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'] ?? '';
if (!CsrfProtection::validateToken($token)) {
die('CSRF 防护触发');
}
// 处理数据...
}
重要注意事项
- 永远不要使用 GET 请求修改服务器状态(增删改操作必须使用 POST/PUT/DELETE)
- 令牌必须使用安全的随机生成器(
random_bytes或openssl_random_pseudo_bytes) - 比较令牌时一定要使用
hash_equals防止时序攻击 - 令牌应该绑定到用户 session(不同用户不同令牌)
- 对于高安全需求,考虑添加 Double Submit Cookie 模式(令牌同时放在 Cookie 和请求体中,服务端对比两者是否一致)
这样基本就能有效防御 CSRF 攻击了。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播