密钥管理最佳实践
约 1527 字大约 5 分钟
secretsmanagement
2025-08-20
概述
密钥(Secrets)包括数据库密码、API 密钥、加密密钥、证书私钥等敏感信息。不当的密钥管理是数据泄露的主要原因之一——据统计,超过 50% 的数据泄露事件与凭证泄露有关。本文将系统介绍密钥管理的核心原则和实践方案。
密钥类型与风险
密钥管理核心原则
1. 永远不要硬编码密钥
# 错误:硬编码密钥
DATABASE_URL = "postgresql://admin:P@ssw0rd@db.example.com/mydb"
API_KEY = "sk-live-abcdef123456789"
AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# 正确:从环境变量读取
import os
DATABASE_URL = os.environ["DATABASE_URL"]
API_KEY = os.environ["API_KEY"]
# 更好的方式:使用密钥管理系统
from vault_client import VaultClient
vault = VaultClient()
DATABASE_URL = vault.get_secret("production/database", "connection_string")2. 密钥轮换策略
HashiCorp Vault
Vault 是最流行的密钥管理解决方案,支持动态密钥、自动轮换和审计日志。
import hvac
# 连接 Vault
client = hvac.Client(
url='https://vault.example.com:8200',
token=os.environ['VAULT_TOKEN'], # 或使用 AppRole 认证
)
# 读取静态密钥
secret = client.secrets.kv.v2.read_secret_version(
path='production/database',
mount_point='secret',
)
db_password = secret['data']['data']['password']
# 使用 AppRole 认证(推荐用于应用)
client.auth.approle.login(
role_id=os.environ['VAULT_ROLE_ID'],
secret_id=os.environ['VAULT_SECRET_ID'],
)
# 动态数据库凭证(自动创建和销毁)
credentials = client.secrets.database.generate_credentials(
name='my-db-role',
mount_point='database',
)
username = credentials['data']['username']
password = credentials['data']['password']
lease_id = credentials['lease_id']
# 凭证在 TTL 过期后自动销毁# Vault 策略(最小权限)
path "secret/data/production/*" {
capabilities = ["read"]
}
path "database/creds/my-db-role" {
capabilities = ["read"]
}
# 禁止访问其他路径
path "secret/data/staging/*" {
capabilities = ["deny"]
}AWS Secrets Manager
import boto3
import json
def get_secret(secret_name: str, region: str = "us-east-1") -> dict:
client = boto3.client('secretsmanager', region_name=region)
response = client.get_secret_value(SecretId=secret_name)
secret = json.loads(response['SecretString'])
return secret
# 使用
db_creds = get_secret("production/database")
connection_string = (
f"postgresql://{db_creds['username']}:{db_creds['password']}"
f"@{db_creds['host']}:{db_creds['port']}/{db_creds['dbname']}"
)
# 自动轮换配置(AWS Lambda 触发)
# aws secretsmanager rotate-secret \
# --secret-id production/database \
# --rotation-lambda-arn arn:aws:lambda:...:function:rotate-db-secret \
# --rotation-rules AutomaticallyAfterDays=30Kubernetes Secrets
# Kubernetes Secret(默认仅 Base64 编码,不是加密)
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
database-url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0BkYjoxMjM0L215ZGI= # base64
api-key: c2stbGl2ZS1hYmNkZWYxMjM0NTY=# 推荐:使用 Sealed Secrets(加密后可安全提交到 Git)
# 安装 Sealed Secrets Controller
# kubeseal 使用集群公钥加密 Secret
# 生成 SealedSecret
# kubeseal --format yaml < secret.yaml > sealed-secret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: app-secrets
namespace: production
spec:
encryptedData:
database-url: AgBy3i4OJSWK+PiTySYZZA9rO... # 加密后的值
api-key: AgCE8YTZK+PiTySYZZA9rO43cGDn...# External Secrets Operator: 从外部密钥管理系统同步
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 15m
secretStoreRef:
name: aws-secretsmanager
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-url
remoteRef:
key: production/database
property: connection_stringgit-crypt 和 SOPS
git-crypt
# 初始化 git-crypt
git-crypt init
# 添加 GPG 密钥
git-crypt add-gpg-user user@example.com
# 指定需要加密的文件
# .gitattributes
# secrets/*.yaml filter=git-crypt diff=git-crypt
# .env.production filter=git-crypt diff=git-crypt
# 锁定/解锁仓库
git-crypt lock
git-crypt unlockSOPS (Secrets OPerationS)
# 使用 AWS KMS 加密
sops --encrypt --kms arn:aws:kms:us-east-1:123:key/abc-def \
secrets.yaml > secrets.enc.yaml
# 使用 AGE 密钥加密
age-keygen -o key.txt
sops --encrypt --age age1ql3z7hjy54pw3hyww5ayyfg7z... \
secrets.yaml > secrets.enc.yaml
# 编辑加密文件(自动解密→编辑→重新加密)
sops secrets.enc.yaml
# 在 CI/CD 中解密
sops --decrypt secrets.enc.yaml > secrets.yaml# SOPS 加密后的文件示例
database:
host: ENC[AES256_GCM,data:mC3p...,type:str]
password: ENC[AES256_GCM,data:kE8r...,type:str]
port: ENC[AES256_GCM,data:NTQz,type:int]
sops:
kms:
- arn: arn:aws:kms:us-east-1:123:key/abc-def
lastmodified: "2024-01-15T10:30:00Z"
version: 3.8.1Pre-commit 密钥检测
# .pre-commit-config.yaml
repos:
# detect-secrets: 检测硬编码密钥
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
# gitleaks: 更全面的密钥泄露检测
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
# trufflehog: 深度扫描 Git 历史
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.63.0
hooks:
- id: trufflehog# 初始化 detect-secrets baseline
detect-secrets scan > .secrets.baseline
# 审计 baseline(标记误报)
detect-secrets audit .secrets.baseline
# CI/CD 中扫描
detect-secrets scan --baseline .secrets.baseline
if [ $? -ne 0 ]; then
echo "New secrets detected! Review before committing."
exit 1
fi
# gitleaks 扫描 Git 历史
gitleaks detect --source . --verbose
gitleaks detect --source . --log-opts="--all" # 扫描所有分支密钥管理架构
密钥泄露应急响应
# 1. 立即轮换泄露的密钥
# 不要等到确认是否被利用,先轮换
# 2. 从 Git 历史中清除(如果提交了密钥)
# 使用 git filter-repo(推荐)或 BFG Repo-Cleaner
pip install git-filter-repo
git filter-repo --replace-text expressions.txt --force
# 3. 扫描 Git 历史确认清除
gitleaks detect --source . --log-opts="--all"
# 4. 强制推送清理后的历史
git push --force-with-lease
# 5. 通知所有开发者重新 clone
# 旧的 clone 仍然包含泄露的密钥最佳实践
- 绝对不要在源代码中硬编码密钥,使用密钥管理系统
- 配置 pre-commit hooks(detect-secrets/gitleaks)拦截密钥提交
- 使用动态密钥(Vault Database Backend),自动创建和销毁临时凭证
- 定期轮换密钥,关键密钥 30-90 天轮换一次
- 最小权限原则,每个服务只能访问其需要的密钥
- 审计日志,记录所有密钥的访问和操作
- Kubernetes 场景使用 External Secrets Operator,不要手动管理 K8s Secrets
- 密钥泄露后立即轮换,并从 Git 历史中彻底清除
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于