背景
參與公司「雲世代」計畫,在 AWS 上實作了一個協助行銷部門加速 EDM 產製的 GenAI 專題。 其中有個功能是讓使用者上傳圖檔,一開始以為寫一支上傳 API 就搞定了,沒想到卡在 S3 的權限設定。
第一個念頭是直接把 Bucket 設成 public —— 簡單,但這等於讓任何人都能存取裡頭的所有檔案,有明顯的資料外洩風險。 改用 presigned URL 之後,由後端產生一組有時效、限定目標物件的網址,使用者在不開放整個 Bucket 的情況下也能安全上傳。
先備知識
閱讀這篇前,建議先對以下 AWS 概念有基本認識:
- S3(Simple Storage Service) — AWS 的物件儲存服務。檔案以「Bucket / Key」結構存放,Bucket 是頂層容器,Key 是物件的路徑名稱。
- Bucket 存取控制 — S3 Bucket 預設為 private,需要明確授權才能存取其中的物件。
- IAM(Identity and Access Management) — AWS 的身份與權限管理服務。每個操作都必須由具備對應權限的 IAM 身份(User 或 Role)來執行。
是什麼
Presigned URL 是一條帶有簽名的 S3 URL,讓沒有 AWS 憑證的人也能在有效期限內對特定物件執行指定操作(下載或上傳)。 簽名是由具備權限的 IAM 身份(通常是後端服務)在伺服器端產生的,有效期過後 URL 自動失效。
重點:Presigned URL 的權限來自「產生它的 IAM 身份」,而非呼叫者本身。
如果 IAM 身份的 S3 權限被撤銷,該 URL 立即失效,即使還在有效期內。
運作原理
產生時,AWS SDK 會把以下資訊打包進 URL 的 query string:
X-Amz-Credential— IAM 身份與 regionX-Amz-Expires— 有效秒數X-Amz-Date— 產生時間(ISO 8601)X-Amz-Signature— HMAC-SHA256 簽名
S3 收到請求時,重新計算簽名比對,並確認是否在有效期內,兩者都通過才執行操作。
兩種用法
GET — 暫時允許下載
最常見的場景:S3 bucket 設為 private,但需要讓用戶或前端短暫存取某個檔案。
import boto3
s3 = boto3.client('s3', region_name='ap-northeast-1')
url = s3.generate_presigned_url(
ClientMethod='get_object',
Params={
'Bucket': 'my-bucket',
'Key': 'reports/2026-05.pdf',
},
ExpiresIn=3600, # 秒,最長 7 天(若用 STS 臨時憑證則最長受 session 限制)
)
# url 是一條帶簽名的 HTTPS URL,直接放到 <a href> 或 fetch() 都可以用
PUT — 讓前端直接上傳到 S3
不想讓檔案先經過你的伺服器再轉到 S3(流量費 + 延遲),可以讓前端拿到 PUT presigned URL 後直接上傳。 後端只負責產生 URL,S3 直接收到檔案。
url = s3.generate_presigned_url(
ClientMethod='put_object',
Params={
'Bucket': 'my-bucket',
'Key': f'uploads/{user_id}/{filename}',
'ContentType': 'image/jpeg', # 要和前端上傳時的 Content-Type 一致
},
ExpiresIn=300, # 上傳用途通常設短一點
)
# 前端用 PUT 把檔案送到這條 URL
# fetch(url, { method: 'PUT', body: file, headers: { 'Content-Type': 'image/jpeg' } })
注意:PUT presigned URL 產生時指定了
ContentType,前端上傳時的 Content-Type header 必須完全一致,否則 S3 會回 403。
安全注意事項
- 有效期設越短越好 — 下載用 1 小時內,上傳用 5–15 分鐘通常已足夠。URL 一旦外洩,任何人都能在期限內使用。
- Key 不要可猜測 — 上傳路徑建議包含 UUID 或 user_id,避免用序號、時間戳等可推測的格式。
- 不要在前端產生 — 產生 URL 需要 AWS 憑證,永遠只能在後端做。
- IAM 最小權限 — 產生 URL 的 IAM role 只需要
s3:GetObject(下載)或s3:PutObject(上傳)on 特定 bucket/prefix,不需要s3:*。 - CORS 設定 — 前端直接 PUT 到 S3 時,bucket 需要設 CORS,允許對應 origin 的 PUT method。
常見坑
- 用 STS 臨時憑證產生的 Presigned URL,有效期不能超過該 STS session 的剩餘時間(即使
ExpiresIn設更長也無效)。 - 如果 IAM 政策走 VPC endpoint,URL 可能只在 VPC 內有效,外部存取會收到 403。
- Bucket 如果開了
require MFA delete或 Object Lock,Presigned URL 無法用來刪除物件。