218 lines
9.6 KiB
Markdown
218 lines
9.6 KiB
Markdown
# 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.elevue.* → 该项目数据库的所有表
|
||
GRANT ALL PRIVILEGES ON `rxthinkcmf.thinkphp6.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(防止运行时修改配置)
|
||
```
|
||
|
||
## 安全检查脚本
|
||
|
||
```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 "=== 检查完成 ==="
|
||
```
|