# 安全防护指南 [TOC] ## 🎯 安全目标 本文档提供了 WeChatDeveloper 项目的安全防护指南,帮助开发者构建安全可靠的微信和支付宝应用。 ## 🔒 核心安全原则 ### 1. 数据保护 - 敏感数据加密存储 - 传输过程使用HTTPS - 定期更新密钥和证书 - 最小权限原则 ### 2. 输入验证 - 所有用户输入必须验证 - 防止SQL注入和XSS攻击 - 参数类型和长度检查 - 特殊字符过滤 ### 3. 身份认证 - 强密码策略 - 多因素认证 - 会话管理 - 权限控制 ## 🛡️ 配置安全 ### 1. 敏感信息保护 ```php <?php // 安全的配置管理 class SecureConfig { private $config; public function __construct() { // 从环境变量读取敏感配置 $this->config = [ 'appid' => getenv('WECHAT_APPID'), 'appsecret' => getenv('WECHAT_APPSECRET'), 'mch_id' => getenv('WECHAT_MCH_ID'), 'mch_key' => getenv('WECHAT_MCH_KEY'), 'cache_path' => getenv('WECHAT_CACHE_PATH') ?: '/tmp/wechat_cache' ]; // 验证必需配置 $this->validateConfig(); } private function validateConfig() { $required = ['appid', 'appsecret', 'mch_id', 'mch_key']; foreach ($required as $key) { if (empty($this->config[$key])) { throw new Exception("缺少必需的配置项: {$key}"); } } } public function get($key, $default = null) { return $this->config[$key] ?? $default; } } ``` ### 2. 环境变量配置 ```bash # .env 文件示例 WECHAT_APPID=wx60a43dd8161666d4 WECHAT_APPSECRET=your_wechat_appsecret WECHAT_MCH_ID=1235704602 WECHAT_MCH_KEY=your_merchant_key WECHAT_CACHE_PATH=/var/cache/wechat WECHAT_DEBUG=false # 数据库配置 DB_HOST=localhost DB_NAME=wechat_app DB_USER=wechat_user DB_PASS=secure_password # Redis配置 REDIS_HOST=127.0.0.1 REDIS_PORT=6379 REDIS_PASS=redis_password ``` ### 3. 文件权限设置 ```bash # 设置安全的文件权限 chmod 600 .env # 配置文件仅所有者可读写 chmod 700 /var/cache/wechat # 缓存目录仅所有者可访问 chmod 644 *.php # PHP文件所有者可写,其他只读 chmod 755 /path/to/wechat # 目录所有者可写,其他可读执行 ``` ## 🔐 数据加密 ### 1. 敏感数据加密 ```php <?php // 数据加密工具类 class DataEncryption { private $key; private $cipher = 'AES-256-GCM'; public function __construct($key = null) { $this->key = $key ?: $this->generateKey(); } public function encrypt($data) { $iv = random_bytes(12); // GCM模式需要12字节IV $tag = ''; $encrypted = openssl_encrypt( $data, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag ); return base64_encode($iv . $tag . $encrypted); } public function decrypt($encryptedData) { $data = base64_decode($encryptedData); $iv = substr($data, 0, 12); $tag = substr($data, 12, 16); $encrypted = substr($data, 28); return openssl_decrypt( $encrypted, $this->cipher, $this->key, OPENSSL_RAW_DATA, $iv, $tag ); } private function generateKey() { return random_bytes(32); // 256位密钥 } } ``` ### 2. 密码哈希 ```php <?php // 安全的密码处理 class PasswordSecurity { public function hash($password) { // 使用password_hash,自动处理salt和成本 return password_hash($password, PASSWORD_ARGON2ID, [ 'memory_cost' => 65536, // 64MB 'time_cost' => 4, // 4次迭代 'threads' => 3 // 3个线程 ]); } public function verify($password, $hash) { return password_verify($password, $hash); } public function needsRehash($hash) { return password_needs_rehash($hash, PASSWORD_ARGON2ID); } } ``` ### 3. 签名验证 ```php <?php // 签名验证工具 class SignatureVerifier { public function verifyWeChatSignature($data, $signature, $key) { // 按字典序排序参数 ksort($data); // 拼接参数 $string = ''; foreach ($data as $k => $v) { if ($k !== 'sign' && $v !== '') { $string .= $k . '=' . $v . '&'; } } $string = rtrim($string, '&') . '&key=' . $key; // 计算签名 $calculatedSignature = strtoupper(md5($string)); return hash_equals($calculatedSignature, $signature); } public function verifyAlipaySignature($data, $signature, $publicKey) { // 移除sign和sign_type参数 unset($data['sign']); unset($data['sign_type']); // 按字典序排序 ksort($data); // 拼接参数 $string = ''; foreach ($data as $k => $v) { if ($v !== '') { $string .= $k . '=' . $v . '&'; } } $string = rtrim($string, '&'); // 验证签名 return openssl_verify( $string, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256 ) === 1; } } ``` ## 🚫 输入验证 ### 1. 参数验证 ```php <?php // 输入验证器 class InputValidator { public function validateWeChatParams($params) { $rules = [ 'appid' => ['required', 'string', 'max:32'], 'appsecret' => ['required', 'string', 'max:64'], 'mch_id' => ['required', 'string', 'max:32'], 'openid' => ['required', 'string', 'max:64'], 'amount' => ['required', 'integer', 'min:1', 'max:1000000'], 'description' => ['required', 'string', 'max:128'] ]; return $this->validate($params, $rules); } public function validate($data, $rules) { $errors = []; foreach ($rules as $field => $ruleList) { $value = $data[$field] ?? null; foreach ($ruleList as $rule) { if (!$this->applyRule($value, $rule, $field)) { $errors[$field][] = "字段 {$field} 验证失败: {$rule}"; } } } return empty($errors) ? true : $errors; } private function applyRule($value, $rule, $field) { switch ($rule) { case 'required': return !empty($value); case 'string': return is_string($value); case 'integer': return is_int($value); case 'max:32': return strlen($value) <= 32; case 'max:64': return strlen($value) <= 64; case 'max:128': return strlen($value) <= 128; case 'min:1': return $value >= 1; case 'max:1000000': return $value <= 1000000; default: return true; } } } ``` ### 2. XSS防护 ```php <?php // XSS防护工具 class XSSProtection { public function sanitize($input) { if (is_array($input)) { return array_map([$this, 'sanitize'], $input); } // HTML实体编码 $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8'); // 移除危险标签 $input = strip_tags($input, '<p><br><strong><em>'); return $input; } public function validateUrl($url) { // 验证URL格式 if (!filter_var($url, FILTER_VALIDATE_URL)) { return false; } // 检查协议 $parsed = parse_url($url); if (!in_array($parsed['scheme'], ['http', 'https'])) { return false; } return true; } } ``` ### 3. SQL注入防护 ```php <?php // SQL注入防护 class SQLInjectionProtection { private $pdo; public function __construct($pdo) { $this->pdo = $pdo; } public function safeQuery($sql, $params = []) { try { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("SQL查询失败: " . $e->getMessage()); return false; } } public function safeInsert($table, $data) { $columns = array_keys($data); $placeholders = ':' . implode(', :', $columns); $sql = "INSERT INTO {$table} (" . implode(', ', $columns) . ") VALUES ({$placeholders})"; return $this->safeQuery($sql, $data); } } ``` ## 🔐 身份认证 ### 1. JWT令牌 ```php <?php // JWT令牌管理 class JWTManager { private $secret; public function __construct($secret) { $this->secret = $secret; } public function generate($payload, $expiry = 3600) { $header = [ 'typ' => 'JWT', 'alg' => 'HS256' ]; $payload['exp'] = time() + $expiry; $payload['iat'] = time(); $headerEncoded = $this->base64UrlEncode(json_encode($header)); $payloadEncoded = $this->base64UrlEncode(json_encode($payload)); $signature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $this->secret, true); $signatureEncoded = $this->base64UrlEncode($signature); return $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded; } public function verify($token) { $parts = explode('.', $token); if (count($parts) !== 3) { return false; } list($headerEncoded, $payloadEncoded, $signatureEncoded) = $parts; $signature = $this->base64UrlDecode($signatureEncoded); $expectedSignature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, $this->secret, true); if (!hash_equals($signature, $expectedSignature)) { return false; } $payload = json_decode($this->base64UrlDecode($payloadEncoded), true); if ($payload['exp'] < time()) { return false; } return $payload; } private function base64UrlEncode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } private function base64UrlDecode($data) { return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); } } ``` ### 2. 会话管理 ```php <?php // 安全的会话管理 class SecureSession { public function start() { // 设置安全的会话参数 ini_set('session.cookie_httponly', 1); ini_set('session.cookie_secure', 1); ini_set('session.use_strict_mode', 1); ini_set('session.cookie_samesite', 'Strict'); session_start(); // 防止会话固定攻击 if (!isset($_SESSION['initiated'])) { session_regenerate_id(true); $_SESSION['initiated'] = true; } // 检查会话超时 if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 1800)) { $this->destroy(); return false; } $_SESSION['last_activity'] = time(); return true; } public function destroy() { $_SESSION = []; if (ini_get("session.use_cookies")) { $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); } session_destroy(); } } ``` ## 🛡️ 访问控制 ### 1. 权限管理 ```php <?php // 权限管理系统 class PermissionManager { private $permissions = []; public function addPermission($role, $resource, $action) { $this->permissions[$role][$resource][] = $action; } public function hasPermission($role, $resource, $action) { return isset($this->permissions[$role][$resource]) && in_array($action, $this->permissions[$role][$resource]); } public function checkAccess($user, $resource, $action) { $userRole = $this->getUserRole($user); return $this->hasPermission($userRole, $resource, $action); } private function getUserRole($user) { // 从数据库或缓存获取用户角色 return $user['role'] ?? 'guest'; } } ``` ### 2. 频率限制 ```php <?php // API频率限制 class RateLimiter { private $redis; public function __construct($redis) { $this->redis = $redis; } public function isAllowed($key, $limit = 100, $window = 3600) { $current = time(); $windowStart = $current - $window; // 清理过期的记录 $this->redis->zRemRangeByScore($key, 0, $windowStart); // 获取当前窗口内的请求数 $count = $this->redis->zCard($key); if ($count < $limit) { // 添加当前请求 $this->redis->zAdd($key, $current, $current . ':' . uniqid()); $this->redis->expire($key, $window); return true; } return false; } } ``` ## 📝 日志和监控 ### 1. 安全日志 ```php <?php // 安全日志记录 class SecurityLogger { private $logFile; public function __construct($logFile = '/var/log/wechat_security.log') { $this->logFile = $logFile; } public function logSecurityEvent($event, $details = []) { $logEntry = [ 'timestamp' => date('Y-m-d H:i:s'), 'event' => $event, 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 'details' => $details ]; file_put_contents( $this->logFile, json_encode($logEntry) . "\n", FILE_APPEND | LOCK_EX ); } public function logFailedLogin($username, $reason) { $this->logSecurityEvent('failed_login', [ 'username' => $username, 'reason' => $reason ]); } public function logSuspiciousActivity($activity, $details) { $this->logSecurityEvent('suspicious_activity', [ 'activity' => $activity, 'details' => $details ]); } } ``` ### 2. 异常监控 ```php <?php // 异常监控系统 class ExceptionMonitor { private $logger; public function __construct($logger) { $this->logger = $logger; } public function handleException($exception) { $context = [ 'message' => $exception->getMessage(), 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString(), 'request_uri' => $_SERVER['REQUEST_URI'] ?? 'unknown', 'request_method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown' ]; $this->logger->error('Exception occurred', $context); // 发送告警(如果是严重错误) if ($this->isCriticalException($exception)) { $this->sendAlert($context); } } private function isCriticalException($exception) { $criticalTypes = [ 'PDOException', 'RedisException', 'WeChat\Exceptions\InvalidResponseException' ]; return in_array(get_class($exception), $criticalTypes); } } ``` ## 🔧 安全配置 ### 1. PHP安全配置 ```ini ; php.ini 安全配置 expose_php = Off display_errors = Off log_errors = On error_log = /var/log/php_errors.log ; 文件上传安全 file_uploads = On upload_max_filesize = 2M max_file_uploads = 20 post_max_size = 8M ; 会话安全 session.cookie_httponly = 1 session.cookie_secure = 1 session.use_strict_mode = 1 session.cookie_samesite = Strict ; 内存和执行时间限制 memory_limit = 256M max_execution_time = 30 max_input_time = 30 ; 禁用危险函数 disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source ``` ### 2. Web服务器配置 ```nginx # Nginx 安全配置 server { listen 443 ssl http2; server_name your-domain.com; # SSL配置 ssl_certificate /path/to/certificate.crt; ssl_certificate_key /path/to/private.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512; ssl_prefer_server_ciphers off; # 安全头 add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; # 隐藏敏感文件 location ~ /\. { deny all; } location ~ \.(env|log|sql)$ { deny all; } # PHP处理 location ~ \.php$ { fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } ``` ## 📚 安全检查清单 ### 开发阶段 - [ ] 所有用户输入都经过验证 - [ ] 敏感数据加密存储 - [ ] 使用参数化查询防止SQL注入 - [ ] 实现适当的错误处理 - [ ] 配置安全的会话管理 - [ ] 实现访问控制和权限管理 ### 部署阶段 - [ ] 使用HTTPS传输 - [ ] 配置安全的文件权限 - [ ] 设置防火墙规则 - [ ] 启用安全日志记录 - [ ] 配置定期备份 - [ ] 更新所有依赖包 ### 运维阶段 - [ ] 定期安全扫描 - [ ] 监控异常活动 - [ ] 及时更新安全补丁 - [ ] 定期审查访问日志 - [ ] 测试备份恢复流程 - [ ] 制定应急响应计划 ## 📚 相关文档 - [性能优化指南](性能优化指南.md) - [常见问题解答](常见问题解答.md) - [部署运维指南](部署运维指南.md) - [安全最佳实践](安全最佳实践.md)