# 11.8 安全加固清单 :::danger 上线前必检 以下安全配置在生产环境上线前必须完成,否则存在严重安全风险。 ::: ## 必须执行 | 项目 | 操作 | 检查命令 | |------|------|----------| | JWT 密钥 | `.env` 中 `JWT.SECRET` 修改为强随机字符串 | `grep JWT.SECRET .env` | | 调试模式 | `.env` 中 `APP_DEBUG = false` | `grep APP_DEBUG .env` | | 错误信息 | `.env` 中 `SHOW_ERROR_MSG = false` | `grep SHOW_ERROR_MSG .env` | | CORS | `.env` 中 `CORS.ALLOW_ORIGIN` 限制为实际域名 | `grep CORS .env` | | 数据库密码 | 修改默认 root 密码 | `mysql -u root -p` | | Redis 密码 | 设置 requirepass | `redis-cli CONFIG GET requirepass` | | HTTPS | 启用 SSL 证书 | `curl -I https://your-domain.com` | ## 生成强密钥 ```bash # ----------------------------------------------------------------------------- # 生成 JWT 签名密钥(32 字节 = 64 个十六进制字符) # ----------------------------------------------------------------------------- # random_bytes(32) → 生成 32 字节的密码学安全随机数 # bin2hex() → 转为十六进制字符串(便于复制粘贴到 .env) php -r "echo bin2hex(random_bytes(32));" # ----------------------------------------------------------------------------- # 生成 MySQL 密码(16 字节 = 32 个十六进制字符) # ----------------------------------------------------------------------------- php -r "echo bin2hex(random_bytes(16));" # ----------------------------------------------------------------------------- # 生成 Redis 密码(16 字节 = 32 个十六进制字符) # ----------------------------------------------------------------------------- php -r "echo bin2hex(random_bytes(16));" ``` ## Nginx 安全配置 ```nginx # ============================================================================= # Nginx 安全加固配置(添加到 server 块内) # ============================================================================= # 禁止访问隐藏文件(.env、.git、.htaccess 等) location ~ /\. { deny all; } # 禁止上传目录执行 PHP(防止 Webshell 攻击) location ~ /uploads/.*\.php$ { deny all; } # ----------------------------------------------------------------------------- # 安全响应头 # ----------------------------------------------------------------------------- # X-Frame-Options → 禁止被嵌入 iframe(防点击劫持) # X-Content-Type-Options → 禁止浏览器 MIME 类型嗅探 # X-XSS-Protection → 启用浏览器 XSS 过滤 # Referrer-Policy → 控制 Referer 头信息泄露 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # 隐藏 Nginx 版本号(防止攻击者针对性利用已知漏洞) server_tokens off; ``` ## PHP 安全配置 ```ini ; ============================================================================= ; php.ini 生产环境安全配置 ; ============================================================================= ; 配置路径:/etc/php/8.2/fpm/php.ini ; 生效命令:sudo systemctl restart php8.2-fpm ; ============================================================================= expose_php = Off ; 隐藏 PHP 版本号(防止针对性攻击) display_errors = Off ; 不向浏览器显示错误(防止 SQL/路径泄露) log_errors = On ; 记录错误到日志文件(便于排查) allow_url_fopen = Off ; 禁止 fopen 打开远程 URL(防 SSRF) disable_functions = exec,passthru,shell_exec,system,proc_open ; 禁用危险系统函数 ``` ## 文件权限 ```bash # ----------------------------------------------------------------------------- # 项目目录(owner 可读写执行,group/others 可读和执行) # ----------------------------------------------------------------------------- chmod -R 755 /www/api/ # 目录 755,文件自动 644 chown -R www-data:www-data /www/api/ # owner 设为 Web 服务器用户 # ----------------------------------------------------------------------------- # 运行时目录(必须可写 — ThinkPHP 缓存、日志、编译模板) # ----------------------------------------------------------------------------- chmod -R 777 /www/api/runtime/ # 777 确保 PHP-FPM 可写入 # ----------------------------------------------------------------------------- # 上传目录(必须可写 — 用户上传文件存储位置) # ----------------------------------------------------------------------------- chmod -R 755 /opt/data/uploads/ # 目录可写 chown -R www-data:www-data /opt/data/uploads/ # owner 设为 Web 服务器用户 # ----------------------------------------------------------------------------- # .env 文件(仅 owner 可读 — 包含数据库密码等敏感信息) # ----------------------------------------------------------------------------- # 600 = owner 可读写,group/others 无权限 chmod 600 /www/api/.env ``` ## 数据库安全 ```sql -- ============================================================================= -- MySQL 安全加固 SQL -- ============================================================================= -- 在 MySQL 命令行中执行:mysql -u root -p -- ============================================================================= -- 修改 root 密码(替换 strong_password_here 为实际强密码) ALTER USER 'root'@'localhost' IDENTIFIED BY 'strong_password_here'; -- 创建应用专用数据库用户(生产环境不应用 root 连接应用) -- rxthinkcmf_app → 应用专用用户名 -- app_password → 应用专用密码 CREATE USER 'rxthinkcmf_app'@'localhost' IDENTIFIED BY 'app_password'; -- 授权应用用户只能访问项目数据库(最小权限原则) -- rxthinkcmf.thinkphp6.antdvue.* → 该项目数据库的所有表 GRANT ALL PRIVILEGES ON `rxthinkcmf.thinkphp6.antdvue`.* TO 'rxthinkcmf_app'@'localhost'; FLUSH PRIVILEGES; -- 刷新权限使配置生效 -- 禁止远程 root 登录(只允许本地连接) -- Host NOT IN (...) → 删除非本地的 root 账户 DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1'); FLUSH PRIVILEGES; ``` ## Redis 安全 ```ini # ============================================================================= # redis.conf 安全配置 # ============================================================================= # 配置路径:/etc/redis/redis.conf # 生效命令:sudo systemctl restart redis # ============================================================================= requirepass your_strong_redis_password ; 设置访问密码(必须设置) bind 127.0.0.1 ; 只监听本地(禁止外部直接访问) protected-mode yes ; 启用保护模式(无密码时拒绝外部连接) # 禁用危险命令(防止误操作清空数据或修改配置) # 将命令重命名为空字符串 = 禁用该命令 rename-command FLUSHALL "" ; 禁用 FLUSHALL(清空所有数据库) rename-command FLUSHDB "" ; 禁用 FLUSHDB(清空当前数据库) rename-command CONFIG "" ; 禁用 CONFIG(防止运行时修改配置) ``` ## 安全检查脚本 ```bash #!/bin/bash # ============================================================================= # check_security.sh — 生产环境安全检查脚本 # ============================================================================= # 用途:自动检查 .env 中的安全配置项是否符合生产要求 # 用法:cd /www/api && bash check_security.sh # ============================================================================= echo "=== 安全检查 ===" # ----------------------------------------------------------------------------- # 检查 APP_DEBUG 是否关闭 # ----------------------------------------------------------------------------- # grep "APP_DEBUG" .env → 从 .env 文件中提取 APP_DEBUG 行 # grep -v "#" → 排除注释行 # cut -d= -f2 → 以 = 为分隔符取第 2 部分(值) # tr -d ' ' → 去除空格 debug=$(grep "APP_DEBUG" .env | grep -v "#" | cut -d= -f2 | tr -d ' ') if [ "$debug" = "true" ]; then echo "❌ APP_DEBUG 未关闭(生产环境必须为 false)" else echo "✓ APP_DEBUG 已关闭" fi # ----------------------------------------------------------------------------- # 检查 JWT 密钥长度 # ----------------------------------------------------------------------------- # sed -n '/\[JWT\]/,/^\[/p' → 提取 .env 中 [JWT] 到下一个 [...] 之间的内容 # grep "^SECRET" → 取 SECRET 行 # ${#secret} → 获取字符串长度 secret=$(sed -n '/\[JWT\]/,/^\[/p' .env | grep "^SECRET" | cut -d= -f2 | tr -d ' ') if [ ${#secret} -lt 32 ]; then echo "❌ JWT 密钥太短(建议32字节以上,当前 ${#secret} 字符)" else echo "✓ JWT 密钥长度足够(${#secret} 字符)" fi # ----------------------------------------------------------------------------- # 检查 CORS 是否允许所有来源 # ----------------------------------------------------------------------------- cors=$(sed -n '/\[CORS\]/,/^\[/p' .env | grep "^ALLOW_ORIGIN" | cut -d= -f2 | tr -d ' ') if [ "$cors" = "*" ]; then echo "⚠ CORS 允许所有来源(生产环境建议限制为实际域名)" else echo "✓ CORS 已限制来源" fi echo "=== 检查完成 ===" ```