本文目录导读:

在 PHP 中实现令牌(Token)安全,主要涉及 CSRF(跨站请求伪造)防护 和 API 身份验证,下面我会分场景详细说明,并给出可直接运行的代码示例。
CSRF 令牌(表单安全)
基础实现(Session 存储)
<?php
session_start();
// 生成令牌
function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 验证令牌
function verifyCsrfToken($token) {
return isset($_SESSION['csrf_token'])
&& hash_equals($_SESSION['csrf_token'], $token);
}
// 使用示例 - 生成表单
$token = generateCsrfToken();
?>
<form method="POST" action="process.php">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($token) ?>">
<!-- 表单其他字段 -->
<button type="submit">提交</button>
</form>
<?php
// 处理提交时验证
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verifyCsrfToken($_POST['csrf_token'] ?? '')) {
die('CSRF 验证失败');
}
// 处理业务逻辑
}
?>
双令牌机制(更安全)
<?php
session_start();
// 生成双令牌
function generateDoubleCsrf() {
$sessionToken = bin2hex(random_bytes(32)); // 存 Session
$valueToken = bin2hex(random_bytes(32)); // 存表单
$_SESSION['csrf_session_token'] = $sessionToken;
$_SESSION['csrf_value_token'] = $valueToken;
return [
'session_token' => $sessionToken,
'value_token' => $valueToken
];
}
// 验证双令牌
function verifyDoubleCsrf($formSessionToken, $formValueToken) {
return hash_equals($_SESSION['csrf_session_token'], $formSessionToken)
&& hash_equals($_SESSION['csrf_value_token'], $formValueToken);
}
// 生成
$tokens = generateDoubleCsrf();
?>
<form method="POST">
<input type="hidden" name="session_token" value="<?= $tokens['session_token'] ?>">
<input type="hidden" name="value_token" value="<?= $tokens['value_token'] ?>">
</form>
API 令牌(身份验证)
Bearer Token(标准做法)
<?php
// 生成 API Token
function generateApiToken($userId) {
$secretKey = '你的服务器端密钥'; // 存环境变量
$payload = json_encode([
'user_id' => $userId,
'exp' => time() + 3600, // 1小时过期
'iat' => time()
]);
$base64Payload = base64_encode($payload);
$signature = hash_hmac('sha256', $base64Payload, $secretKey);
return $base64Payload . '.' . $signature;
}
// 验证 API Token
function verifyApiToken($token) {
$parts = explode('.', $token);
if (count($parts) !== 2) return false;
[$base64Payload, $signature] = $parts;
$expectedSignature = hash_hmac('sha256', $base64Payload, $secretKey);
// 验证签名
if (!hash_equals($expectedSignature, $signature)) {
return false;
}
// 验证过期时间
$payload = json_decode(base64_decode($base64Payload), true);
if ($payload['exp'] < time()) {
return false;
}
return $payload['user_id'];
}
// 使用中间件验证请求
function authenticateRequest() {
$headers = getallheaders();
$token = str_replace('Bearer ', '', $headers['Authorization'] ?? '');
$userId = verifyApiToken($token);
if (!$userId) {
http_response_code(401);
echo json_encode(['error' => '未授权']);
exit;
}
return $userId;
}
更安全的 JWT(推荐)
<?php
// 使用 Firebase JWT 库(composer require firebase/php-jwt)
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
function createJWT($userId) {
$payload = [
'iss' => 'your-site.com',
'sub' => $userId,
'iat' => time(),
'exp' => time() + 3600
];
return JWT::encode($payload, $_ENV['JWT_SECRET'], 'HS256');
}
function verifyJWT($token) {
try {
$decoded = JWT::decode($token, new Key($_ENV['JWT_SECRET'], 'HS256'));
return $decoded->sub; // 返回用户ID
} catch (Exception $e) {
return false;
}
}
一次性令牌(防重放攻击)
<?php
// 生成一次性令牌
function generateOneTimeToken() {
$token = bin2hex(random_bytes(32));
$_SESSION['one_time_tokens'][$token] = time();
// 清理过期的令牌
foreach ($_SESSION['one_time_tokens'] as $t => $time) {
if (time() - $time > 300) { // 5分钟过期
unset($_SESSION['one_time_tokens'][$t]);
}
}
return $token;
}
// 验证并消耗令牌
function verifyOneTimeToken($token) {
if (isset($_SESSION['one_time_tokens'][$token])) {
unset($_SESSION['one_time_tokens'][$token]); // 立即销毁
return true;
}
return false;
}
// 用于关键操作,如密码重置链接
$resetToken = generateOneTimeToken();
$link = "https://example.com/reset?token=" . $resetToken;
安全最佳实践
使用随机数生成器
// ✅ 安全 $token = bin2hex(random_bytes(32)); // ❌ 不安全 $token = md5(uniqid(rand(), true)); // 可预测
恒定时间比较
// ✅ 安全(防时序攻击)
if (hash_equals($expected, $actual)) { /* 成功 */ }
// ❌ 不安全
if ($expected === $actual) { /* 容易被时序攻击 */ }
安全存储
// 对于 Token 存储:
// - 不要明文存数据库,存储 hash
// - 使用 password_hash() 或 hash_hmac()
$hashedToken = hash_hmac('sha256', $token, $secretKey);
// 仅存这个 hash 到数据库
完整示例(CSRF + 双提交 Cookie)
<?php
// 完整的安全表单防护类
class SecureForm {
private $secret;
public function __construct() {
$this->secret = $_ENV['CSRF_SECRET'] ?? 'change-me';
session_start();
}
public function generateToken() {
$id = session_id();
$timestamp = time();
$token = hash_hmac('sha256', $id . $timestamp, $this->secret);
setcookie('csrf_cookie', $token, 0, '/', '', true, true);
return $token;
}
public function validateToken($formToken) {
$cookieToken = $_COOKIE['csrf_cookie'] ?? '';
// 双重验证:Cookie 和 表单值都要匹配
return hash_equals($cookieToken, $formToken)
&& $this->verifyTimestamp($formToken);
}
private function verifyTimestamp($token) {
// 可以验证生成时间,比如只允许10分钟内有效
return true; // 简化处理
}
}
总结要点
| 场景 | 推荐方法 | 注意事项 |
|---|---|---|
| 表单提交 | Session + CSRF 令牌 | 每次登录后重新生成 |
| API 验证 | JWT (HS256) | 设置合理过期时间 |
| 高危操作 | 一次性令牌 | 使用后立即销毁 |
| 密码重置 | 随机短令牌 | 加时间限制 |
最重要的安全原则:
- 永远不要自创加密算法
- 使用
random_bytes()生成随机数 - 使用
hash_equals()做比较 - 令牌要有过期时间
- 敏感操作要二次验证
选择哪种方案取决于你的具体需求,但记住:安全没有银弹,需要结合实际场景综合使用。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播