常见安全配置错误
典型配置风险
默认密码
路由器/摄像头/数据库使用出厂默认凭据(admin/admin、root/root)。Mirai 僵尸网络 2016 年感染 50 万台 IoT 设备,全部因使用默认密码。任何系统上线前必须更改所有默认凭据。
调试模式开启
生产环境开启 DEBUG=True(Flask/Django),会暴露完整堆栈跟踪、源代码、配置变量,甚至允许在浏览器中直接执行代码。使用环境变量区分开发和生产配置,生产环境务必关闭调试模式。
目录列表
Nginx/Apache 开启目录浏览(autoindex on),攻击者可以枚举服务器文件,发现备份文件(.bak)、配置文件(.env)、源码压缩包。务必禁用,只暴露必要的 URL 路径。
详细错误信息
生产环境返回数据库错误详情(表名、字段名、SQL 语句),帮助攻击者了解系统架构。生产环境应返回通用错误消息,错误详情只记录到内部日志。
云存储公开访问
AWS S3 / GCS Bucket 设置为公开可读,是近年数据泄露的重灾区。默认配置应为私有,使用 Pre-signed URL 临时授权访问,定期审计存储桶权限。
HTTPS 与 TLS 最佳配置
# Nginx TLS 安全配置(2024 年推荐)
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# 只允许 TLS 1.2 和 1.3,禁用不安全的 TLS 1.0/1.1/SSL
ssl_protocols TLSv1.2 TLSv1.3;
# 强加密套件(前向保密 ECDHE,认证加密 GCM)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS:365 天强制 HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Session 缓存与前向保密
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off; # 禁用 Session Tickets 保证前向保密
# OCSP Stapling(加速证书验证)
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 valid=300s;
}
敏感数据加密
传输中加密(In-Transit)
传输中的数据通过 TLS 加密,上面 Nginx 配置已覆盖。额外注意:
- 内部服务间通信(微服务)同样需要 mTLS 或 TLS,不能因为是"内网"就明文传输
- 数据库连接使用 TLS(
sslmode=require),防止内网嗅探 - 第三方 API 调用必须验证证书(不要
verify=False)
静态加密(At-Rest)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
# AES-256-GCM:认证加密(同时保证保密性和完整性)
# 密钥长度:256 位 = 32 字节
class EncryptionService:
def __init__(self, key: bytes):
assert len(key) == 32, "AES-256 密钥必须是 32 字节"
self.aesgcm = AESGCM(key)
def encrypt(self, plaintext: str) -> bytes:
nonce = os.urandom(12) # GCM 标准 nonce 为 12 字节,每次必须随机!
ciphertext = self.aesgcm.encrypt(nonce, plaintext.encode(), None)
return nonce + ciphertext # 将 nonce 与密文一起存储(nonce 不需要保密)
def decrypt(self, data: bytes) -> str:
nonce, ciphertext = data[:12], data[12:]
plaintext = self.aesgcm.decrypt(nonce, ciphertext, None)
# 如果数据被篡改,decrypt 会抛出 InvalidTag 异常(认证失败)
return plaintext.decode()
# 使用示例(密钥从环境变量获取,见下文密钥管理)
import base64
key = base64.b64decode(os.environ['ENCRYPTION_KEY'])
enc = EncryptionService(key)
encrypted = enc.encrypt("用户的银行卡号")
original = enc.decrypt(encrypted) # "用户的银行卡号"
密钥管理:永远不要硬编码 Secret
最危险的错误:代码中的密钥
# 极度危险!Secret 硬编码在代码中
DATABASE_URL = "postgresql://user:mypassword123@db.example.com/prod"
SECRET_KEY = "my-super-secret-jwt-key"
AWS_SECRET = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# 这些 Secret 一旦提交到 Git,即使后来删除,仍然在 Git 历史中
# GitHub 的 Secret Scanning 会自动检测并通知安全团队
# 正确:使用环境变量
import os
DATABASE_URL = os.environ['DATABASE_URL']
SECRET_KEY = os.environ['SECRET_KEY']
环境变量管理(.env 文件)
# .env 文件(本地开发,永远不提交到 Git!)
DATABASE_URL=postgresql://user:pass@localhost/mydb
SECRET_KEY=dev_only_secret_key
REDIS_URL=redis://localhost:6379
# .gitignore 中必须包含:
.env
.env.local
.env.production
*.pem
*_rsa
*_rsa.pub
# 检查是否有 Secret 被意外提交到 Git 历史
# 使用 trufflehog 扫描 Git 历史
docker run --rm -it trufflesecurity/trufflehog:latest \
git file:///path/to/repo --only-verified
# 如果发现 Secret 被提交:
# 1. 立即轮换(更换)该 Secret(假设已被泄露)
# 2. 在 git 历史中删除(git filter-branch 或 BFG Repo-Cleaner)
# 3. 强制推送(需要通知团队成员)
生产环境密钥管理
HashiCorp Vault
开源的专用 Secret 管理工具。支持动态 Secret(每次请求生成临时数据库凭据,用后即失效)、加密即服务(应用不需要处理密钥,直接调用 Vault 加密 API)、审计日志(记录所有 Secret 访问)。
AWS Secrets Manager
AWS 托管的 Secret 存储服务。自动轮换数据库密码(支持 RDS)、KMS 加密、IAM 精细访问控制。成本:约 $0.40/Secret/月。适合已在 AWS 上的应用。
Kubernetes Secrets
K8s 原生 Secret 资源,以 Base64 编码(非加密!)存储。必须配合 etcd 加密(Encryption at Rest)和 RBAC 限制访问。更好的方案:使用 External Secrets Operator 对接 Vault/AWS SM。
GitHub Actions Secrets
在仓库 Settings → Secrets 中配置,CI/CD 流水线通过
${{ secrets.MY_SECRET }} 引用。Secret 在日志中自动脱敏,Fork PR 中不可访问(防止 PR 注入攻击)。安全 HTTP Headers 完整配置清单
安全 HTTP 响应头是浏览器安全的重要防线,告诉浏览器如何安全地处理页面内容。
# Nginx:完整的安全 Headers 配置
# 防止 XSS(限制脚本来源)
add_header Content-Security-Policy
"default-src 'self'; script-src 'self' 'nonce-{nonce}'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
# 防止点击劫持(禁止在 iframe 中嵌入)
add_header X-Frame-Options "DENY" always;
# 防止 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# 控制 Referer 信息泄露
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 禁用浏览器某些危险特性(权限策略)
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# 强制 HTTPS(已在上文配置)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 跨域隔离(启用高精度计时器等特性,需配合 COOP/COEP)
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
# 隐藏服务器版本信息
server_tokens off;
| Header | 作用 | 推荐值 |
|---|---|---|
Content-Security-Policy |
防 XSS,限制资源加载来源 | 按需配置,至少设 default-src 和 object-src |
X-Frame-Options |
防点击劫持,禁止 iframe 嵌入 | DENY 或 SAMEORIGIN |
X-Content-Type-Options |
禁止浏览器猜测 MIME 类型 | nosniff |
Strict-Transport-Security |
强制 HTTPS,防止降级攻击 | max-age=31536000; includeSubDomains |
Referrer-Policy |
控制 Referer 头信息泄露 | strict-origin-when-cross-origin |
Permissions-Policy |
禁用不需要的浏览器 API | 禁用摄像头/麦克风/地理位置(若不需要) |
快速检测你的网站安全 Headers
在线工具 securityheaders.com 可以快速检测任意网站的安全 Headers 配置情况,并给出评分(A+ 到 F)和改进建议。Mozilla Observatory(observatory.mozilla.org)提供更全面的安全评估,包括 TLS 配置、Cookie 安全等。
本章小结
安全配置是"防守最后一公里":① 生产环境关闭调试模式、默认账号,最小化暴露面;② TLS 使用 1.2+,配置前向保密加密套件,启用 HSTS;③ 敏感数据用 AES-256-GCM 加密存储,密钥从 Vault/KMS 获取,永远不硬编码在代码或版本库中;④ 完整配置安全 HTTP Headers(CSP、HSTS、X-Frame-Options 等),几行配置就能阻挡多种客户端攻击。安全配置不需要额外编写代码,却能以最低成本提供显著的安全保障。