生成预签名 URL 并验证
目标
通过创建、使用并让预签名 URL 过期,掌握这种不让字节经过应用服务器、只委托访问权限的方式。
为什么重要
由应用服务器中转用户文件下载的实现,在小规模时运行良好。问题出在 100 个 500MB 的视频被同时请求的时候:应用服务器要中转 50GB,工作进程被占满,带宽被应用服务器的规格限制,缓冲处理不当还会 OOM。预签名 URL 把这种结构彻底颠倒过来——应用只判断权限并生成一个只有几个字节的签名 URL,真正的传输直接发生在客户端与存储之间。在第 7 步把 URL 拆开来看,就能明白为什么这样是安全的:签名由密钥(secret key)生成,但密钥本身不在 URL 的任何位置。所以即使 URL 泄露,暴露的也只限于那个对象、那个操作和那个过期时间。
步骤
- 把
/opt/fixtures/s3/readme.txt上传到s3://lab-media/private/secret.txt。 - 不带签名执行
curl -o /dev/null -w '%{http_code}' http://127.0.0.1:9000/lab-media/private/secret.txt,并在/root/ps/anon.txt中写入status=403。 - 用
/root/ps/presign.py生成过期时间为 300 秒的预签名 GET URL,并以一行保存到/root/ps/get_url.txt。URL 中必须有X-Amz-Signature和X-Amz-Expires。 - 用该 URL 下载,生成
/root/ps/downloaded.txt。内容必须与原文件相同。 - 生成一个过期时间为 1 秒的 URL,等待 3 秒后再使用。在
/root/ps/expired.txt中写入status=403。 - 用过期时间为 300 秒的预签名 PUT URL,把
/opt/fixtures/s3/sales.csv上传到s3://lab-media/upload/direct.csv。用aws --profile local s3api head-object确认它已存在。 - 在
/root/ps/sig.md中写一张 Markdown 表格。行标题是X-Amz-Algorithm、X-Amz-Credential、X-Amz-Date、X-Amz-Expires、X-Amz-SignedHeaders、X-Amz-Signature六个,每一行写出该参数的含义。文件中不得出现密钥的值。
参考
- 凭据是
/opt/fixtures/s3/creds.env中的S3_*值,aws profilelocal与s3-basics第 1 步用同样的方法创建。 aws s3 presign也能生成 URL,但 CLI v1 默认使用旧签名(SigV2,Signature=),不会出现 X-Amz-* 项。本实验使用可以固定签名版本的 boto3 来生成。- 在 shell 中使用 URL 时,务必用引号括起来——
&会被解释为后台运行。 - 过期时间越短越安全。下载通常是几分钟,上传通常是几十分钟。
- 常见错误 1:没有给预签名 PUT 设置大小和 Content-Type 限制——客户端就能上传任何内容。
- 常见错误 2:把预签名 URL 原样写进日志——在过期之前任何人都可以使用。
准备私有对象
把 /opt/fixtures/s3/readme.txt 上传到 s3://lab-media/private/secret.txt。
沿用上一项实验的存储桶,或新建一个。默认存储桶禁止匿名访问。
确认匿名访问被拒绝
不带签名执行 curl -o /dev/null -w '%{http_code}' http://127.0.0.1:9000/lab-media/private/secret.txt,并在 /root/ps/anon.txt 中写入 status=403。
不带签名,直接用 URL 访问试试。记录下状态码是什么。
生成预签名 GET URL
用 /root/ps/presign.py 生成过期时间为 300 秒的预签名 GET URL,并以一行保存到 /root/ps/get_url.txt。URL 中必须有 X-Amz-Signature 和 X-Amz-Expires。
同时指定过期时间。URL 上附带与签名相关的查询参数才算正常。
用预签名 URL 实际下载
用该 URL 下载,生成 /root/ps/downloaded.txt。内容必须与原文件相同。
不用引号括起来的话,shell 会把 & 符号解释为后台运行。
确认过期的 URL 被拒绝
生成一个过期时间为 1 秒的 URL,等待 3 秒后再使用。在 /root/ps/expired.txt 中写入 status=403。
把过期时间设得非常短,稍等一会儿再使用。记录下被拒绝的状态码。
用预签名 PUT 直接上传
用过期时间为 300 秒的预签名 PUT URL,把 /opt/fixtures/s3/sales.csv 上传到 s3://lab-media/upload/direct.csv。用 aws --profile local s3api head-object 确认它已存在。
由客户端直接上传到存储。关键在于不经过应用服务器。
分析签名要素
在 /root/ps/sig.md 中写一张 Markdown 表格。行标题是 X-Amz-Algorithm、X-Amz-Credential、X-Amz-Date、X-Amz-Expires、X-Amz-SignedHeaders、X-Amz-Signature 六个,每一行写出该参数的含义。文件中不得出现密钥的值。
把 URL 的查询参数拆开,写出每个参数分别是什么约束。确认 URL 中没有密钥。