コンテンツにスキップ

Securely Distribute Millions of S3 Files with CloudFront Signed Cookies + a Single Signed URL

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

S3 上にある大量のプライベートファイルを、ファイルごとの S3 presigned URL ではなく、CloudFront signed cookies と単一の signed URL を組み合わせて配布する設計記事。 ユーザーには短い有効期限の manifest 用 signed URL を渡し、manifest 内の signed cookies によって対象プレフィックス配下の大量ファイルへアクセスさせる。 S3 は非公開、CloudFront は OAC で S3 にアクセス、KMS で暗号化、署名鍵は CloudFront key group で管理する。

本文

記事の課題設定は、外部ユーザーに対して数百万件規模の S3 ファイルを安全に配布したいというもの。 S3 バケットは private、オブジェクトは SSE-KMS で暗号化されている。 ユーザーごと、リクエストごと、テナントごとにファイル群を渡す必要がある。

単純な解は S3 presigned URL をファイルごとに発行すること。 しかし、数百万ファイルになると URL 生成、配布、期限管理、クライアント UX、監査、バケット名露出、KMS との整合性などがつらくなる。 大量の URL を一覧化して渡すよりも、ユーザーには一つの入り口だけを渡し、その先のファイル群は CloudFront 側で認可したい。

CloudFront-first の構成

記事の設計は CloudFront を配布の入口にする。 S3 は直接公開せず、CloudFront だけが S3 にアクセスできる。 CloudFront から S3 へのアクセスには OAC、Origin Access Control を使う。

構成の中心は二層の認可。

一つ目は、manifest ファイルへの CloudFront signed URL。 ユーザーに渡すのは、処理済み manifest JSON への一つの URL だけ。 この URL は短い TTL を持ち、manifest を取得するためだけに使われる。

二つ目は、ファイル本体への CloudFront signed cookies。 manifest の中に、対象プレフィックスへアクセスするための Cookie 情報を含める。 ユーザーが manifest を取得した後、クライアントはその Cookie を付けてファイル群へアクセスする。 CloudFront は Cookie の署名、期限、Resource 条件を検証し、許可されたプレフィックス配下だけを通す。

パス設計

ファイル本体は、テナントやリクエスト単位のプレフィックスにまとめる。

/data/<tenant>/<request>/*

manifest は別のパスに置く。

/manifest/<tenant>/<request>/processed/manifest.json

ファイル配布用の Cookie は /data/<tenant>/<request>/* のような wildcard resource に対して発行する。 manifest 用 signed URL は、manifest JSON 一つだけに対して発行する。

この分離により、ユーザーへ渡す URL は一つで済む一方、実際のファイル群には Cookie ベースで広い範囲のアクセス権を与えられる。 ファイルごとの URL を作らなくてよい。

manifest の役割

manifest は、ユーザーがダウンロードすべきファイル一覧とアクセス情報を持つ。 元の raw manifest をアップロードし、それを Lambda が処理して、署名済み Cookie や期限、basePath、メタデータを含む processed manifest を作る。

manifest のイメージは次のような構造。

{
  "datasetId": "dataset-123",
  "tenantId": "tenant-a",
  "requestId": "req-456",
  "generatedAt": "2026-01-01T00:00:00Z",
  "expiresAt": "2026-01-02T00:00:00Z",
  "access": {
    "basePath": "https://cdn.example.com/data/tenant-a/req-456/",
    "cookies": {
      "CloudFront-Policy": "...",
      "CloudFront-Signature": "...",
      "CloudFront-Key-Pair-Id": "..."
    }
  },
  "files": [
    {
      "path": "part-00001.parquet",
      "size": 123456,
      "contentType": "application/octet-stream"
    }
  ]
}

実際の manifest はファイル一覧やメタデータを持ち、クライアントは basePath と相対パスを組み合わせてファイルを取得する。

signed cookies のポリシー

CloudFront signed cookies では、custom policy を使う。 ポリシーには、許可する Resource、期限、必要なら IP 制限を含める。

{
  "Statement": [
    {
      "Resource": "https://cdn.example.com/data/tenant-a/req-456/*",
      "Condition": {
        "DateLessThan": {
          "AWS:EpochTime": 1767312000
        }
      }
    }
  ]
}

DateLessThan は UTC epoch seconds。 必要なら IpAddress 条件を追加し、特定のクライアント IP 範囲からのアクセスだけに制限できる。

Cookie は以下のような 3 要素になる。

CloudFront-Policy
CloudFront-Signature
CloudFront-Key-Pair-Id

CloudFront はリクエスト時にこれらを検証し、ポリシーに合う URL だけを通す。 S3 側にはユーザーの認可ロジックを持たせず、CloudFront edge で認可する。

signed URL と signed cookies の使い分け

manifest には signed URL を使う。 理由は、最初にユーザーへ渡す入口が一つでよいから。 manifest の TTL は短めにする。 たとえば 15 分から 60 分程度。

ファイル本体には signed cookies を使う。 理由は、対象ファイルが大量で、プレフィックス配下にまとめてアクセスさせたいから。 Cookie の TTL は manifest より長くできる。 たとえば数時間から数日。

manifest への URL が漏れても期限が短い。 ファイルへのアクセスは Cookie とポリシーで制御される。 権限を取り消したい場合は、新しいプレフィックスを使う、Cookie を再発行する、manifest を無効化する、CloudFront invalidation を使うなどの手段がある。

S3、OAC、KMS

S3 バケットは public にしない。 CloudFront から S3 へのアクセスは OAC を使う。 S3 バケットポリシーでは CloudFront distribution からのアクセスだけを許可する。

SSE-KMS を使う場合、KMS key policy も重要。 CloudFront OAC 経由の読み取り、アップロード処理をする writer の kms:Encrypt、必要な kms:Decrypt を適切に許可する。 KMS ポリシーが不足すると、CloudFront や Lambda の署名・配布設計が正しくても、オブジェクト取得時に失敗する。

署名鍵管理

CloudFront の署名には key group と public key/private key を使う。 private key はクライアントやフロントエンドコードに置かない。 Lambda や安全な署名サービスだけが private key を使って signed URL / signed cookies を発行する。

鍵ローテーションでは、複数の public key を key group に登録しておき、新旧鍵を一定期間重ねる。 古い Cookie や URL が期限切れになるまで旧鍵も許可し、その後に削除する。 これにより、既存ダウンロードを急に壊さずに鍵を更新できる。

障害と運用

期限切れの場合、CloudFront edge で拒否される。 ユーザーには manifest を再取得させるか、署名を再発行する。

一部ファイルが欠けている場合、manifest の対象ファイルだけが失敗する。 すべての認可をやり直すのではなく、欠けたオブジェクトの有無、KMS、S3 パス、CloudFront キャッシュを確認する。

配布量が多い場合は、CloudFront のキャッシュ、Origin Shield、tiered caching を検討する。 大量ファイルを S3 から毎回直接読むと origin 側に負荷がかかるため、配布パターンに応じたキャッシュ設計が必要。

読み替え

この設計は、大量オブジェクト配布で「URL をファイルごとに作る」発想から、「権限付きのプレフィックスを配る」発想に切り替えるもの。 データエクスポート、ログ配布、学習データセット配布、テナント別レポート配布などで使える。

ポイントは、ユーザーに渡す入口を manifest signed URL に集約し、実ファイルへの権限を signed cookies で束ねること。 S3 は非公開にし、認可は CloudFront edge に寄せる。

要点

  • ファイルごとの S3 presigned URL は、大量ファイル配布では生成・管理・UX が重い。
  • CloudFront signed URL は manifest への単一入口に使う。
  • CloudFront signed cookies はプレフィックス配下の大量ファイルアクセスに使う。
  • S3 は private、CloudFront は OAC 経由で S3 にアクセスする。
  • SSE-KMS を使う場合は KMS key policy まで含めて設計する。
  • private key は署名用サービスや Lambda に閉じ、クライアントへ出さない。
  • signed URL と signed cookies の TTL を分けることで、入口とファイルアクセスの寿命を調整できる。

タグ

aws #cloudfront #s3 #signed-cookies #security #distribution