在 AWS 上使用 Terraform 构建安全的 Serverless 上传模式 🚀
Source: Dev.to
(请提供您希望翻译的文章内容,我将为您翻译成简体中文。)
一个真实的案例(我一直在看到的)
几周前,我审查了一个系统,用户在该系统中上传文件(有些超过 300 MB)。
原始流程看起来“合理”:
- 前端将文件上传到后端
- 后端处理请求
- 后端将文件上传到 S3
- 后端响应
实际上,系统彻底失败:
- ❌ 超时
根本原因始终相同:在无服务器架构中,后端绝不应该处理文件上传。
修复一切的模式
S3 预签名 URL
与其通过后端路由文件,不如让客户端直接使用 受控、临时且安全 的预签名 URL 上传到 S3。这正是许多生产级无服务器应用采用的模式。
什么是预签名 URL(通俗解释)?
预签名 URL 是一种有时间限制的签名请求,拥有该 URL 的任何人都可以执行特定的 S3 操作(例如 PutObject)。URL 过期后就失效,无法再使用。
Lambda:生成预签名 URL(Node.js)
// src/index.mjs
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: "us-east-1" });
export const handler = async (event) => {
const { fileName, contentType } = JSON.parse(event.body);
const key = `uploads/${Date.now()}-${fileName}`;
const command = new PutObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: key,
ContentType: contentType,
});
const uploadUrl = await getSignedUrl(s3, command, {
expiresIn: 300, // 5 minutes
});
return {
statusCode: 200,
body: JSON.stringify({ uploadUrl, key }),
};
};
Security Layers (What Makes This Production‑Ready)
- Short‑lived URLs – 限制为几分钟。
- IAM Least‑Privilege – Lambda 角色只能
s3:PutObject到arn:aws:s3:::my-bucket/uploads/*。 - Private S3 Bucket – 没有公共访问。
- Sanitized Object Names – 在服务器端生成,以避免路径遍历。
- Proper CORS Configuration – 仅允许预期的来源进行上传。
⚠️ Important: CORS 通常是导致上传失败的首要因素;确保在存储桶上正确配置它。
基础设施即代码(Infrastructure as Code)使用 Terraform
所有资源均在 Terraform 中定义。仓库结构如下:
aws-s3-presigned-url-lambda-terraform/
├── 01-s3.tf
├── 02-lambda.tf
├── 04-api.tf
├── client/
│ ├── index.html
│ ├── index.html.tpl
│ ├── logo-client.png
│ └── logo-s3.png
├── dev.tfvars
├── drawio/
│ ├── aws-s3-presignend.gif
│ ├── aws-s3-url.drawio
│ ├── image-2.png
│ └── image.png
├── lambda-function.zip
├── main.tf
├── Makefile
├── provider.tf
├── README.md
├── security/
│ ├── checkov.yaml
│ └── trivy.yaml
├── src/
│ ├── index.mjs
│ ├── node_modules/
│ ├── package-lock.json
│ └── package.json
├── terraform.tfstate
├── terraform.tfstate.backup
├── tfplan
├── tfplan.json
├── variables.tf
└── versions.tf
核心组件包括:
- S3 存储桶,设置了受限访问和 CORS 配置。
- Lambda 函数,用于生成预签名 URL。
- API Gateway(或 HTTP API),向客户端公开
/presign端点。
运行 PoC 的要求
- macOS(或任何已安装 AWS CLI、Node.js 和 Terraform 的类 Unix 环境)。
- 除了克隆仓库并运行
terraform apply外,无需其他设置。
完整可运行的 PoC
完整实现可在以下地址获取:
https://github.com/francotel/aws-s3-presigned-url-lambda-terraform
克隆仓库,使用 Terraform 部署,即可拥有完整的无服务器上传流程。
最终思考
如果你的后端仍然处理文件上传,你会付出更高的成本且扩展效率更低。使用预签名 URL 将繁重的工作转移到 S3,降低后端负载,并遵循最佳实践的安全模式。
参考文献
- AWS Blog: Securing Amazon S3 presigned URLs for serverless applications
文章讨论了最佳实践,例如校验和验证、过期策略以及最小权限的 IAM 策略。