引言:API安全的重要性
在当今前后端分离和微服务架构盛行的时代,API已成为系统间通信的核心枢纽。然而,不安全的API可能导致:
- 数据泄露:敏感信息被非法获取
- 篡改风险:传输数据被中间人修改
- 重放攻击:请求被恶意重复使用
- DDoS攻击:系统资源被耗尽
本文将深入探讨PHP API安全设计的四大核心要素,并提供完整的实现方案。
一、API安全四要素详解
1. Token授权机制
作用:验证客户端身份,确保请求来源合法
实现代码
// Token生成与验证类
class TokenManager {
const TOKEN_EXPIRE = 3600; // 1小时过期
public static function generateToken($userId) {
$token = bin2hex(random_bytes(32)); // 生成64字符随机token
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->setex("api:token:$token", self::TOKEN_EXPIRE, $userId);
return $token;
}
public static function verifyToken($token) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis->get("api:token:$token");
}
}
// 使用示例
$userId = 123;
$token = TokenManager::generateToken($userId);
// 客户端后续请求需在Header中携带: Authorization: Bearer {$token}
2. 时间戳超时机制
作用:防止请求被截获后重放攻击
参数 | 说明 | 建议值 |
---|---|---|
timestamp | 请求发起时的UNIX时间戳 | 当前时间 |
timeout | 请求有效时间(秒) | 300(5分钟) |
实现代码
class RequestValidator {
const REQUEST_TIMEOUT = 300; // 5分钟
public static function checkTimestamp($timestamp) {
$currentTime = time();
if (abs($currentTime - $timestamp) > self::REQUEST_TIMEOUT) {
throw new Exception("请求已过期", 400);
}
return true;
}
}
// 使用示例
$requestTime = $_SERVER['HTTP_X_TIMESTAMP'] ?? null;
RequestValidator::checkTimestamp($requestTime);
3. 签名机制
作用:确保请求参数不被篡改
签名生成步骤:
- 获取所有请求参数
- 排除sign参数本身
- 按参数名排序
- 拼接成字符串
- 添加密钥(盐值)
- 使用加密算法生成签名
实现代码
class Signature {
const SECRET_KEY = 'your_api_secret_123!@#';
public static function generate(array $params) {
unset($params['sign']); // 移除sign参数
ksort($params); // 按键名排序
$queryString = http_build_query($params);
$stringToSign = $queryString . self::SECRET_KEY;
return hash_hmac('sha256', $stringToSign, self::SECRET_KEY);
}
public static function verify(array $params) {
if (!isset($params['sign'])) {
return false;
}
$clientSign = $params['sign'];
$serverSign = self::generate($params);
return hash_equals($serverSign, $clientSign);
}
}
// 使用示例
$params = [
'user_id' => 123,
'action' => 'get_profile',
'timestamp' => time(),
'nonce' => bin2hex(random_bytes(8))
];
$params['sign'] = Signature::generate($params);
// 验证签名
if (!Signature::verify($_GET)) {
http_response_code(401);
die('Invalid signature');
}
4. 随机数(Nonce)防重放
作用:确保同一请求不能被重复使用
参数 | 说明 | 存储方式 |
---|---|---|
nonce | 一次性随机字符串(16-32位) | Redis/数据库 |
实现代码
class NonceManager {
const NONCE_EXPIRE = 3600; // 1小时过期
public static function generate() {
return bin2hex(random_bytes(16)); // 32字符随机字符串
}
public static function checkAndSave($nonce) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists("api:nonce:$nonce")) {
throw new Exception("请求已处理", 400);
}
$redis->setex("api:nonce:$nonce", self::NONCE_EXPIRE, 1);
return true;
}
}
// 使用示例
$nonce = $_POST['nonce'] ?? null;
NonceManager::checkAndSave($nonce);
二、完整API安全方案实现
1. 客户端请求示例
class ApiClient {
const API_KEY = 'client_123';
const API_SECRET = 'secret_456';
public function request($url, $params = []) {
// 基础参数
$baseParams = [
'key' => self::API_KEY,
'timestamp' => time(),
'nonce' => bin2hex(random_bytes(8)),
'version' => '1.0'
];
// 合并参数
$requestParams = array_merge($baseParams, $params);
// 生成签名
$requestParams['sign'] = $this->generateSign($requestParams);
// 发送请求
return $this->httpPost($url, $requestParams);
}
private function generateSign($params) {
ksort($params);
unset($params['sign']);
$queryString = http_build_query($params);
$stringToSign = $queryString . self::API_SECRET;
return hash_hmac('sha256', $stringToSign, self::API_SECRET);
}
private function httpPost($url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
// 使用示例
$client = new ApiClient();
$result = $client->request('https://api.example.com/user/info', [
'user_id' => 123
]);
2. 服务端验证实现
class ApiServer {
const API_SECRETS = [
'client_123' => 'secret_456'
];
public function handleRequest() {
try {
// 验证基本参数
$this->checkRequiredParams(['key', 'timestamp', 'nonce', 'sign']);
// 获取参数
$params = $_POST;
$apiKey = $params['key'];
$timestamp = $params['timestamp'];
$nonce = $params['nonce'];
$clientSign = $params['sign'];
// 1. 验证API Key
if (!isset(self::API_SECRETS[$apiKey])) {
throw new Exception('Invalid API key', 401);
}
// 2. 验证时间戳
if (abs(time() - $timestamp) > 300) {
throw new Exception('Request expired', 400);
}
// 3. 验证Nonce
$this->checkNonce($nonce);
// 4. 验证签名
$serverSign = $this->generateSign($params, self::API_SECRETS[$apiKey]);
if (!hash_equals($serverSign, $clientSign)) {
throw new Exception('Invalid signature', 401);
}
// 验证通过,处理业务逻辑
return $this->processRequest($params);
} catch (Exception $e) {
http_response_code($e->getCode() ?: 500);
return [
'status' => 'error',
'message' => $e->getMessage()
];
}
}
private function checkRequiredParams($required) {
foreach ($required as $param) {
if (!isset($_POST[$param])) {
throw new Exception("Missing parameter: $param", 400);
}
}
}
private function checkNonce($nonce) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
if ($redis->exists("api:nonce:$nonce")) {
throw new Exception('Duplicate request', 400);
}
$redis->setex("api:nonce:$nonce", 3600, 1);
}
private function generateSign($params, $secret) {
ksort($params);
unset($params['sign']);
$queryString = http_build_query($params);
$stringToSign = $queryString . $secret;
return hash_hmac('sha256', $stringToSign, $secret);
}
private function processRequest($params) {
// 业务逻辑处理
return [
'status' => 'success',
'data' => [
'user_id' => $params['user_id'] ?? null,
'info' => '...'
]
];
}
}
// 使用示例
$server = new ApiServer();
$response = $server->handleRequest();
header('Content-Type: application/json');
echo json_encode($response);
三、进阶安全措施
1. HTTPS传输加密
# Nginx配置示例
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 强制TLS 1.2+
ssl_protocols TLSv1.2 TLSv1.3;
# 安全加密套件
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384...';
# HSTS头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
# 传递给PHP处理
}
}
2. 请求频率限制
class RateLimiter {
const RATE_LIMIT = 100; // 每分钟100次
const RATE_LIMIT_PERIOD = 60; // 60秒
public static function check($apiKey, $ip) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = "api:rate_limit:$apiKey:$ip";
$current = $redis->get($key);
if ($current && $current >= self::RATE_LIMIT) {
throw new Exception('Rate limit exceeded', 429);
}
$redis->multi();
$redis->incr($key);
$redis->expire($key, self::RATE_LIMIT_PERIOD);
$redis->exec();
}
}
// 使用示例
RateLimiter::check($apiKey, $_SERVER['REMOTE_ADDR']);
3. 敏感数据加密
class DataEncryptor {
const ENCRYPTION_KEY = 'your_encryption_key_32bytes';
const ENCRYPTION_METHOD = 'aes-256-gcm';
public static function encrypt($data) {
$iv = random_bytes(openssl_cipher_iv_length(self::ENCRYPTION_METHOD));
$tag = '';
$encrypted = openssl_encrypt(
json_encode($data),
self::ENCRYPTION_METHOD,
self::ENCRYPTION_KEY,
0,
$iv,
$tag
);
return base64_encode($iv . $tag . $encrypted);
}
public static function decrypt($data) {
$data = base64_decode($data);
$ivLength = openssl_cipher_iv_length(self::ENCRYPTION_METHOD);
$iv = substr($data, 0, $ivLength);
$tag = substr($data, $ivLength, 16);
$encrypted = substr($data, $ivLength + 16);
return json_decode(openssl_decrypt(
$encrypted,
self::ENCRYPTION_METHOD,
self::ENCRYPTION_KEY,
0,
$iv,
$tag
), true);
}
}
四、常见攻击与防御
攻击类型 | 攻击描述 | 防御措施 |
---|---|---|
重放攻击 | 截获并重复有效请求 | 时间戳+Nonce机制 |
中间人攻击 | 拦截篡改传输数据 | HTTPS+签名验证 |
DDoS攻击 | 大量请求耗尽服务器资源 | 速率限制+IP黑名单 |
SQL注入 | 通过输入执行恶意SQL | 参数化查询+输入过滤 |
XSS攻击 | 注入恶意脚本 | 输出编码+Content Security Policy |
五、最佳实践总结
- 分层防御:不要依赖单一安全措施,采用多层次防护
- 最小权限原则:只授予必要的API访问权限
- 定期轮换密钥:定期更换API密钥和加密密钥
- 详细日志记录:记录所有API请求用于审计和分析
- API版本控制:通过版本号管理接口变更
- 输入验证:严格验证所有输入参数
- 错误处理:避免暴露敏感信息的错误消息
通过实施这些安全措施,您的PHP API将能够抵御大多数常见攻击,确保数据传输的安全性和完整性。