AWS Secrets Manager:如何在 Python Lambda 中设置机密并获取它们
Source: Dev.to
管理敏感信息(如数据库密码、API 密钥和令牌)是构建安全云应用的关键环节。将机密硬编码在源代码或配置文件中是一种常见的反模式,会导致安全漏洞。
AWS Secrets Manager 提供了一种安全、可扩展且可审计的方式,在运行时动态存储和检索机密。本文将介绍:
- 什么是 AWS Secrets Manager
- 如何创建和存储机密
- 所需的 IAM 权限
- 如何在 Python AWS Lambda 中获取机密
- 最佳实践
1. 什么是 AWS Secrets Manager?
AWS Secrets Manager 是一项托管服务,帮助您:
- 安全存储机密(凭证、API 密钥、令牌)
- 使用 AWS KMS 加密机密
- 通过 IAM 控制访问
- 自动轮换机密(针对受支持的服务)
- 在运行时以编程方式检索机密
典型使用场景:
- 数据库凭证(RDS、Aurora)
- 第三方 API 密钥
- JWT 签名密钥
- OAuth 客户端密钥
2. 在 AWS Secrets Manager 中创建密钥
步骤 1:打开 AWS Secrets Manager
- 登录 AWS 控制台。
- 导航至 Secrets Manager。
- 点击 存储新密钥。
步骤 2:选择密钥类型
选择 其他类型的密钥 来存储自定义键/值对,例如:
DB_USERNAME = admin
DB_PASSWORD = StrongPassword@123
DB_HOST = mydb.cluster-xyz.us-east-1.rds.amazonaws.com
Secrets Manager 将这些值存储为加密的 JSON。
步骤 3:配置加密
- 选择默认的 AWS‑managed KMS 密钥,或
- 选择客户管理的 KMS 密钥以满足更严格的合规要求。
步骤 4:命名密钥
为密钥指定清晰且与环境相关的名称,例如:
myapp/dev/databasemyapp/staging/databasemyapp/prod/database
此命名策略可避免意外的跨环境访问。
步骤 5:审查并创建
点击 存储。您的密钥现在已安全存储。
3. Lambda 的 IAM 权限
您的 Lambda 函数必须拥有读取密钥的权限。
IAM 策略示例
将以下策略附加到 Lambda 执行角色:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/dev/database*"
}
]
}
重要: 始终将 Resource 限定为特定的密钥,而不要使用 "*"。
4. 在 Python Lambda 中获取 Secrets
步骤 1:Python 依赖
AWS Lambda 已经内置了 boto3 和 botocore。无需额外的库。
步骤 2:获取 Secrets 的 Python 代码
import json
import boto3
from botocore.exceptions import ClientError
def get_secret(secret_name, region_name="us-east-1"):
client = boto3.client(
service_name="secretsmanager",
region_name=region_name
)
try:
response = client.get_secret_value(SecretId=secret_name)
except ClientError as e:
raise RuntimeError(f"Unable to retrieve secret: {e}")
# Secrets are usually stored as JSON strings
if "SecretString" in response:
return json.loads(response["SecretString"])
else:
# Binary secrets (rare case)
return response["SecretBinary"]
步骤 3:在 Lambda 处理函数中使用 Secrets
def lambda_handler(event, context):
secret_name = "myapp/dev/database"
secrets = get_secret(secret_name)
db_user = secrets["DB_USERNAME"]
db_password = secrets["DB_PASSWORD"]
db_host = secrets["DB_HOST"]
# Example usage
print(f"Connecting to DB at {db_host} with user {db_user}")
return {
"statusCode": 200,
"body": "Secrets fetched successfully"
}
5. 性能考虑(重要)
每次调用 GetSecretValue 都会进行网络请求。为降低延迟和 API 使用量,请在模块级别缓存密钥:
_cached_secrets = None
def get_cached_secret(secret_name):
global _cached_secrets
if _cached_secrets is None:
_cached_secrets = get_secret(secret_name)
return _cached_secrets
6. 基于环境的密钥管理
使用环境变量来控制加载哪个密钥:
import os
secret_name = os.environ["SECRET_NAME"] # e.g., myapp/dev/database
secrets = get_cached_secret(secret_name)
这使得:
- 在 DEV / STAGE / PROD 环境中使用相同的代码库
- 环境特定的密钥
- 更安全的部署
7. Security and Best Practices
- 永不硬编码密钥。
- 使用最小权限的 IAM 策略。
- 为每个环境保持独立的密钥。
- 在支持的情况下启用自动轮换。
- 在 Lambda 中缓存密钥以提升性能。
- 谨慎记录日志——绝不记录密钥值。
- 对于高度敏感的数据,优先使用 Secrets Manager 而非 SSM 参数存储。