Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_EleVue/zh/deploy/security.md
T
2026-10-08 17:50:29 +08:00

220 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 11.8 安全加固清单
:::danger 上线前必检
以下安全配置在生产环境上线前必须完成,否则存在严重安全风险。
:::
## 必须执行
| 项目 | 操作 | 检查命令 |
|------|------|----------|
| JWT 密钥 | `.env` 中 `JWT_SECRET` 修改为强随机字符串(≥32 字符,缺失/过短时服务拒绝签发令牌,fail-close) | `grep JWT_SECRET .env` |
| 调试模式 | `.env` 中 `APP_DEBUG=false` | `grep APP_DEBUG .env` |
| 运行环境 | `.env` 中 `APP_ENV=production` | `grep APP_ENV .env` |
| CORS | `.env` 中 `CORS_ALLOW_ORIGIN` 限制为实际域名 | `grep CORS_ALLOW_ORIGIN .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.3/fpm/php.ini
; 生效命令:sudo systemctl restart php8.3-fpm
; =============================================================================
expose_php = Off ; 隐藏 PHP 版本号(防止针对性攻击)
display_errors = Off ; 不向浏览器显示错误(防止 SQL/路径泄露)
log_errors = On ; 记录错误到日志文件(Laravel 日志在 storage/logs/)
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 服务器用户
# -----------------------------------------------------------------------------
# 运行时目录(必须可写 — Laravel 缓存、日志、编译视图)
# -----------------------------------------------------------------------------
chmod -R 777 /www/api/storage/ # 777 确保 PHP-FPM 可写入
chmod -R 777 /www/api/bootstrap/cache/ # 配置/路由缓存写入位置
# -----------------------------------------------------------------------------
# 上传目录(必须可写 — 用户上传文件存储位置)
# -----------------------------------------------------------------------------
chmod -R 755 /www/api/public/uploads/ # 目录可写
chown -R www-data:www-data /www/api/public/uploads/ # owner 设为 Web 服务器用户
# -----------------------------------------------------------------------------
# .env 文件(仅 owner 可读 — 包含数据库密码、JWT 密钥等敏感信息)
# -----------------------------------------------------------------------------
# 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.laravel13.elevue.* → 该项目数据库的所有表
GRANT ALL PRIVILEGES ON `rxthinkcmf.laravel13.elevue`.* 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(防止运行时修改配置)
```
:::warning Redis 承载关键状态
本项目的会话、缓存、队列和 JWT 令牌状态均存储在 Redis 中,Redis 泄露等同于会话泄露。除密码外,还应限制网络访问(安全组 / bind 内网),并避免与其它业务共用实例。
:::
## 安全检查脚本
```bash
#!/bin/bash
# =============================================================================
# check_security.sh — 生产环境安全检查脚本
# =============================================================================
# 用途:自动检查 .env 中的安全配置项是否符合生产要求
# 用法:cd /www/api && bash check_security.sh
# =============================================================================
echo "=== 安全检查 ==="
# -----------------------------------------------------------------------------
# 检查 APP_DEBUG 是否关闭
# -----------------------------------------------------------------------------
# grep "^APP_DEBUG" .env → 从 .env 中取 APP_DEBUG 行(^ 锚定行首,排除注释)
# cut -d= -f2 → 以 = 为分隔符取第 2 部分(值)
# tr -d ' ' → 去除空格
debug=$(grep "^APP_DEBUG" .env | cut -d= -f2 | tr -d ' ')
if [ "$debug" = "true" ]; then
echo "❌ APP_DEBUG 未关闭(生产环境必须为 false)"
else
echo "✓ APP_DEBUG 已关闭"
fi
# -----------------------------------------------------------------------------
# 检查 JWT 密钥长度(本项目 .env 为扁平键名 JWT_SECRET)
# -----------------------------------------------------------------------------
# ${#secret} → 获取字符串长度
secret=$(grep "^JWT_SECRET" .env | cut -d= -f2 | tr -d ' ')
if [ ${#secret} -lt 32 ]; then
echo "❌ JWT 密钥太短(建议 32 字符以上,当前 ${#secret} 字符)"
else
echo "✓ JWT 密钥长度足够(${#secret} 字符)"
fi
# -----------------------------------------------------------------------------
# 检查 CORS 是否允许所有来源
# -----------------------------------------------------------------------------
cors=$(grep "^CORS_ALLOW_ORIGIN" .env | cut -d= -f2 | tr -d ' ')
if [ "$cors" = "*" ]; then
echo "⚠ CORS 允许所有来源(生产环境建议限制为实际域名)"
else
echo "✓ CORS 已限制来源"
fi
echo "=== 检查完成 ==="
```