利用 CloudFront 訪問 S3 的私有內容

🌏 Read the English version


為什麼使用 CloudFront 訪問 S3 私有內容?

核心價值與三大優勢

在 AWS 雲端架構中,Amazon S3 與 Amazon CloudFront 的結合是內容分發的黃金組合。特別是在處理私有內容(如付費媒體、內部文件、受保護的資源)時,這種架構能同時滿足「安全性」、「效能」、「成本」三大需求。

1. 全球加速與低延遲

CloudFront 擁有遍佈全球的邊緣節點(Edge Locations),當使用者請求內容時:

  • 就近存取:自動路由到最近的邊緣節點
  • 快取機制:首次請求後內容被快取在邊緣節點,後續請求直接從快取提供
  • 延遲降低:相較於直接訪問 S3,延遲可降低 50-80%
  • 頻寬優化:減少回源(Origin)請求,降低 S3 出站流量成本

實際案例:若 S3 存儲桶位於美國東部(us-east-1),亞洲使用者直接訪問延遲約 200-300ms;透過 CloudFront 東京節點,延遲可降至 20-50ms。

2. 強化安全性與存取控制

直接開放 S3 存儲桶為公開訪問存在重大安全風險。透過 CloudFront + OAI/OAC 方案:

  • S3 保持私有:存儲桶完全不對外開放,僅允許 CloudFront 訪問
  • 單一入口:所有請求必須經過 CloudFront,無法繞過直接訪問 S3
  • 簽名 URL:可配合 CloudFront Signed URLs 實現時效性存取控制
  • WAF 整合:CloudFront 可整合 AWS WAF,阻擋惡意請求、DDoS 攻擊
  • 地理限制:可設定特定國家/地區的存取限制

3. 成本優化與流量管理

  • 降低 S3 請求費用:CloudFront 快取減少對 S3 的 GET 請求次數
  • 出站流量成本:CloudFront 出站流量費率通常低於 S3 直接傳輸
  • 免費方案:AWS Free Tier 每月提供 1TB CloudFront 出站流量與 1000 萬次請求
  • 可預測費用:透過 TTL 設定控制快取時間,平衡成本與即時性

Origin Access Identity (OAI) vs Origin Access Control (OAC)

技術演進與選擇建議

AWS 提供兩種方式讓 CloudFront 安全訪問 S3:OAI(舊方案)與 OAC(新方案)。

Origin Access Identity (OAI) – 傳統方案

工作原理

  • 建立一個特殊的 CloudFront 身份(OAI)
  • 在 S3 Bucket Policy 中授權該 OAI 讀取權限
  • CloudFront 使用 OAI 身份向 S3 請求內容

限制

  • ❌ 不支援 S3 伺服器端加密(SSE-KMS)
  • ❌ 不支援動態請求(POST, PUT, DELETE)
  • ❌ AWS 官方已標註為 Legacy(傳統功能)

Origin Access Control (OAC) – 新一代方案(推薦)

增強功能

  • ✅ 完整支援 S3 SSE-KMS 加密
  • ✅ 支援所有 HTTP 方法(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
  • ✅ 更強的安全性(使用 AWS Signature Version 4)
  • ✅ 更細緻的權限控制

建議:新專案請直接使用 OAC;舊專案建議逐步遷移至 OAC。


完整配置步驟(使用 OAC)

步驟 1:建立 S3 存儲桶

# 使用 AWS CLI 建立存儲桶
aws s3 mb s3://my-private-content-bucket --region us-east-1

# 確保阻擋所有公開存取
aws s3api put-public-access-block 
  --bucket my-private-content-bucket 
  --public-access-block-configuration 
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

步驟 2:上傳測試內容

# 上傳測試檔案
echo "This is a private file" > test.txt
aws s3 cp test.txt s3://my-private-content-bucket/test.txt

# 確認無法直接訪問(應返回 403)
curl https://my-private-content-bucket.s3.amazonaws.com/test.txt

步驟 3:建立 CloudFront Distribution 與 OAC

進入 CloudFront ConsoleCreate Distribution

  1. Origin Settings
    • Origin Domain:選擇你的 S3 存儲桶(my-private-content-bucket.s3.us-east-1.amazonaws.com)
    • Origin Access:選擇 Origin Access Control Settings (recommended)
    • 點擊 Create New OAC,使用預設設定
  2. Default Cache Behavior
    • Viewer Protocol Policy:Redirect HTTP to HTTPS(強制 HTTPS)
    • Allowed HTTP Methods:選擇 GET, HEAD(唯讀)或 GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE(讀寫)
    • Cache Policy:CachingOptimized(推薦)
  3. Settings
    • Price Class:選擇地理範圍(Use All Edge Locations / Use Only North America and Europe)
    • Alternate Domain Names (CNAMEs):若有自訂網域,在此輸入
    • Custom SSL Certificate:若使用自訂網域,需上傳 SSL 憑證
  4. 點擊 Create Distribution

步驟 4:複製並套用 S3 Bucket Policy

CloudFront 建立完成後,會顯示一個 橘色提示框,點擊 Copy Policy 按鈕,會得到類似以下的 JSON 策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-private-content-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234EXAMPLE"
        }
      }
    }
  ]
}

前往 S3 Console → 選擇存儲桶 → PermissionsBucket Policy → 貼上策略 → Save

步驟 5:驗證訪問

# 取得 CloudFront Domain Name(例如 d123abc456def.cloudfront.net)
CLOUDFRONT_DOMAIN="d123abc456def.cloudfront.net"

# 透過 CloudFront 訪問(應成功)
curl https://${CLOUDFRONT_DOMAIN}/test.txt

# 直接訪問 S3(應失敗,返回 403)
curl https://my-private-content-bucket.s3.amazonaws.com/test.txt

進階配置:CloudFront Signed URLs

實現時效性存取控制

若需要更細緻的權限控制(如付費內容、限時下載連結),可使用 CloudFront Signed URLs

配置步驟

1. 建立 CloudFront Key Pair

  1. 登入 AWS 根帳戶(Root Account)
  2. 前往 Security CredentialsCloudFront Key PairsCreate New Key Pair
  3. 下載私鑰(pk-XXXXX.pem)並記錄 Access Key ID

2. 設定 CloudFront Distribution 為 Restricted

  • 編輯 Distribution → Behaviors → 選擇 Default Behavior
  • Restrict Viewer Access:選擇 Yes
  • Trusted Signers:選擇 Self(使用自己的 AWS 帳戶)

3. 使用 Python 生成 Signed URL

from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
import base64
import json

def create_signed_url(url, key_pair_id, private_key_path, expiration_minutes=60):
    # 設定過期時間
    expiration = datetime.utcnow() + timedelta(minutes=expiration_minutes)
    expiration_timestamp = int(expiration.timestamp())

    # 建立策略
    policy = {
        "Statement": [{
            "Resource": url,
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": expiration_timestamp
                }
            }
        }]
    }

    policy_json = json.dumps(policy, separators=(',', ':'))
    policy_b64 = base64.b64encode(policy_json.encode()).decode().replace('+', '-').replace('=', '_').replace('/', '~')

    # 載入私鑰
    with open(private_key_path, 'rb') as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
            backend=default_backend()
        )

    # 簽名
    signature = private_key.sign(policy_json.encode(), padding.PKCS1v15(), hashes.SHA1())
    signature_b64 = base64.b64encode(signature).decode().replace('+', '-').replace('=', '_').replace('/', '~')

    # 組合 Signed URL
    signed_url = f"{url}?Policy={policy_b64}&Signature={signature_b64}&Key-Pair-Id={key_pair_id}"
    return signed_url

# 使用範例
url = "https://d123abc456def.cloudfront.net/premium/video.mp4"
key_pair_id = "APKAXXXXXXXXXXXXXXXX"
private_key_path = "/path/to/pk-XXXXX.pem"

signed_url = create_signed_url(url, key_pair_id, private_key_path, expiration_minutes=30)
print(f"Signed URL (30分鐘有效): {signed_url}")

常見問題 FAQ

Q1: CloudFront 訪問 S3 返回 403 Forbidden,如何排查?

可能原因與解決方案

  1. Bucket Policy 未正確設定
    • 確認 Resource 是否包含 /*(例如 arn:aws:s3:::bucket-name/*
    • 確認 AWS:SourceArn 的 Distribution ID 是否正確
  2. S3 區塊公開存取設定
    • 確認沒有阻擋 CloudFront 的訪問(應只阻擋公開存取,不影響 OAC)
  3. CloudFront Distribution 尚未部署完成
    • 檢查 Distribution Status 是否為 Deployed(初次建立需 15-20 分鐘)
  4. 快取問題
    • 建立 Invalidation 清除快取:aws cloudfront create-invalidation --distribution-id E1234 --paths "/*"

Q2: 如何確認請求是否真的來自 CloudFront?

驗證方法

  1. 檢查 S3 Access Logs
    • 啟用 S3 Access Logging
    • 檢查 Requester ARN 是否包含 cloudfront.amazonaws.com
  2. CloudFront Access Logs
    • 啟用 CloudFront Logging,查看 x-edge-location, x-edge-result-type 欄位
  3. 使用自訂 Header 驗證
    • 在 CloudFront Behavior 中加入自訂 Header(例如 X-Custom-Secret: abc123
    • 在應用層檢查此 Header,只接受帶有正確 Header 的請求

Q3: OAI 與 OAC 可以共存嗎?如何遷移?

答案:可以共存,但建議逐步遷移

遷移步驟

  1. 在現有 Distribution 中建立新的 Origin,使用 OAC
  2. 修改 Behavior,將部分路徑(如 /new/*)指向新 Origin
  3. 逐步測試,確認功能正常
  4. 更新 S3 Bucket Policy,同時授權 OAI 與 OAC(過渡期)
  5. 確認所有流量已切換至 OAC 後,移除 OAI 相關設定

Q4: 如何設定不同檔案類型的快取時間?

使用 Cache Behaviors 與 TTL 設定

  1. 前往 CloudFront Distribution → BehaviorsCreate Behavior
  2. 設定 Path Pattern(例如 *.jpg, *.png, *.css
  3. 設定 TTL(Time To Live):
    • 靜態資源(圖片、CSS、JS):Minimum TTL = 86400(1天),Maximum TTL = 31536000(1年)
    • 動態內容(API 回應):Minimum TTL = 0,Maximum TTL = 3600(1小時)

範例:針對圖片設定長快取

Path Pattern: *.jpg
Cache Policy: CachingOptimized
Minimum TTL: 86400 (1 day)
Maximum TTL: 31536000 (1 year)
Default TTL: 2592000 (30 days)

Q5: CloudFront + S3 如何處理版本控制(Cache Busting)?

三種常見方案

  1. 檔名加入版本號或 Hash(推薦)
    • 例如:style.v2.css, app.a3f2b1c.js
    • 優點:不需清除快取,新版本自動生效
  2. Query String 版本控制
    • 例如:style.css?v=2
    • 需在 CloudFront 設定中啟用 Query String Forwarding
  3. 手動 Invalidation
    • 使用 AWS CLI:aws cloudfront create-invalidation --distribution-id E1234 --paths "/style.css"
    • 注意:每月前 1000 次 Invalidation 免費,超過部分每次 $0.005

Q6: 如何監控 CloudFront 的成本與流量?

使用 CloudWatch 與 Cost Explorer

  1. CloudWatch Metrics
    • Requests:總請求次數
    • BytesDownloaded:下載流量
    • BytesUploaded:上傳流量(POST, PUT)
    • 4xxErrorRate, 5xxErrorRate:錯誤率
  2. 設定 CloudWatch Alarm
    aws cloudwatch put-metric-alarm 
      --alarm-name cloudfront-high-error-rate 
      --metric-name 5xxErrorRate 
      --namespace AWS/CloudFront 
      --statistic Average 
      --period 300 
      --threshold 5 
      --comparison-operator GreaterThanThreshold 
      --evaluation-periods 2 
      --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts
    
  3. Cost Explorer 分析
    • 前往 AWS Cost Explorer → 選擇 Service = CloudFront
    • 分析各地區的流量費用
    • 識別異常流量峰值

Q7: 如何整合 WAF 防護 CloudFront?

AWS WAF 配置步驟

  1. 建立 Web ACL
    • 前往 AWS WAF Console → Create Web ACL
    • Resource Type:選擇 CloudFront
  2. 新增規則
    • Rate Limiting:限制同一 IP 每 5 分鐘最多 2000 次請求
    • Geo Blocking:阻擋特定國家(如需要)
    • SQL Injection / XSS 防護:使用 AWS Managed Rules
  3. 關聯到 CloudFront
    • 編輯 CloudFront Distribution → Security → AWS WAF Web ACL → 選擇剛建立的 ACL

範例:Rate Limiting 規則

{
  "Name": "RateLimitRule",
  "Priority": 1,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 2000,
      "AggregateKeyType": "IP"
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "RateLimitRule"
  }
}

最佳實踐

安全性

  • 強制 HTTPS:Viewer Protocol Policy 設為 “Redirect HTTP to HTTPS”
  • 啟用 S3 版本控制:防止意外刪除或覆蓋重要檔案
  • 啟用 S3 Object Lock:保護關鍵資料不被刪除(合規需求)
  • 定期輪換 CloudFront Key Pair:若使用 Signed URLs,建議每年更換金鑰
  • 啟用 CloudTrail:記錄所有 API 操作,便於稽核

效能優化

  • 啟用 Gzip/Brotli 壓縮:在 CloudFront Behavior 中啟用 “Compress Objects Automatically”
  • 使用 HTTP/2 與 HTTP/3:CloudFront 預設支援,確保瀏覽器相容
  • 選擇合適的 Price Class:若主要使用者在特定地區,可選擇較少邊緣節點降低成本
  • 設定合理的 TTL:根據內容更新頻率調整快取時間

成本控制

  • 使用 S3 Intelligent-Tiering:自動將不常訪問的物件移至低成本儲存層
  • 設定 S3 Lifecycle Policy:自動刪除過期的臨時檔案
  • 監控 Invalidation 使用量:超過免費額度前考慮使用版本控制方案
  • 分析流量來源:識別並阻擋異常流量(如爬蟲、攻擊)

故障排除指南

問題:CloudFront 回傳 504 Gateway Timeout

可能原因

  • S3 回應時間過長(大型檔案首次請求)
  • 網路連線問題

解決方案

  1. 增加 CloudFront Origin Response Timeout(預設 30 秒,可調整至 60 秒)
  2. 檢查 S3 存儲桶是否在高負載狀態
  3. 考慮使用 S3 Transfer Acceleration

問題:快取命中率(Cache Hit Ratio)過低

可能原因

  • 請求帶有過多 Query String 參數或 Headers
  • TTL 設定過短

解決方案

  1. 檢查 Cache Key Settings,僅轉發必要的 Query Strings 與 Headers
  2. 調整 Default TTL,延長快取時間
  3. 使用 CloudFront Cache Policy 統一管理快取規則

問題:部分使用者無法訪問(特定地區)

可能原因

  • 啟用了 Geo Restriction
  • WAF 規則誤擋

解決方案

  1. 檢查 CloudFront Distribution → Restrictions → Geographic Restrictions
  2. 檢查 WAF Web ACL Logs,確認是否有誤判規則
  3. 暫時將問題 IP 加入 WAF 白名單測試

總結

透過 CloudFront 訪問 S3 私有內容是 AWS 雲端架構的最佳實踐,能同時實現:

  • 🚀 全球加速:低延遲、高頻寬的內容分發
  • 🔒 強化安全:S3 完全私有,僅允許 CloudFront 訪問
  • 💰 成本優化:減少 S3 請求費用與出站流量成本
  • 🛡️ 進階防護:整合 WAF、Signed URLs、地理限制

關鍵要點

  1. 新專案請使用 OAC(Origin Access Control)而非舊的 OAI
  2. 務必正確設定 S3 Bucket Policy,確保僅授權特定 CloudFront Distribution
  3. 根據內容類型設定合理的 TTL,平衡即時性與快取效益
  4. 啟用 CloudWatch 監控CloudTrail 稽核,及時發現異常
  5. 定期檢視 Cost Explorer,優化成本結構

這套架構適用於各種場景:媒體串流、軟體下載、付費內容、企業內部文件、API 資源等。透過正確的配置與持續優化,能打造高效、安全、經濟的雲端內容分發系統。

相關文章

Leave a Comment