Cookie・セッション管理シリーズ 第4回 / 全5回
CloudFront署名付きCookieで実現するロールベースのS3ファイル配信
Cookieシリーズの応用編。ログイン済みユーザーのロールに応じてS3上のファイルのダウンロードを許可したい。ただし、ファイル本体をAPIサーバーから転送するのではなく、CloudFrontから直接配信したい。
この記事では、APIが認可を判断して署名付きCookieを発行し、ブラウザがCloudFrontにアクセスする構成を考える。
1. 全体アーキテクチャ
役割分担は次の通り。
| コンポーネント | 責務 |
|---|---|
| ブラウザ | APIからCookieを受け取り、CloudFrontへのリクエストに添付する |
| APIサーバー | 認証、ユーザーロールの判定、許可対象の決定、ポリシーへの署名 |
| CloudFront | 署名・ポリシー・期限を検証し、許可されたリクエストだけ配信する |
| S3 | 非公開オブジェクトを保持し、OAC経由のCloudFrontアクセスを許可する |
CloudFrontにロール情報を送って判定させるわけではない。APIが判定結果をCloudFrontの署名付きポリシーへ変換するのが設計の核心だ。
2. リクエストの流れ
CloudFrontがキャッシュヒットした場合、S3へのリクエストは発生しない。アクセス制限の検証は、キャッシュの有無とは別に必要になる。
3. 署名付きCookieの中身
CloudFrontのカスタムポリシーを使うと、アクセス可能なリソースや期限を指定できる。
Copied!!{ "Statement": [ { "Resource": "https://download.example.com/files/team-a/*", "Condition": { "DateLessThan": { "AWS:EpochTime": 1794708000 } } } ] }
AWS:EpochTime はUNIX時刻(秒)。上の値は説明用であり、実装では現在時刻から短い期限を計算する。
APIはこのポリシーを秘密鍵で署名し、次の3つのCookieを発行する。
Copied!!Set-Cookie: CloudFront-Policy=<base64エンコード済みポリシー>; Path=/files/; Secure; HttpOnly; SameSite=Lax Set-Cookie: CloudFront-Signature=<署名>; Path=/files/; Secure; HttpOnly; SameSite=Lax Set-Cookie: CloudFront-Key-Pair-Id=<公開鍵ID>; Path=/files/; Secure; HttpOnly; SameSite=Lax
CloudFront-Policy、CloudFront-Signature、CloudFront-Key-Pair-Id がカスタムポリシー用の3点セット。CloudFrontディストリビューションの制限付きビヘイビアに信頼されたキーグループを設定し、APIが保管する秘密鍵に対応する公開鍵を登録する。
重要: この3つのCookieがCloudFrontに送信されるには、Cookieの Domain と Path が配信URLに一致する必要がある。APIとCloudFrontが別のホスト名なら、ホスト名の設計が必要だ。
4. APIとCloudFrontのドメイン設計
例えば以下の構成を考える。
- API:
api.example.com - ファイル配信:
download.example.com
api.example.com のレスポンスで Domain=example.com を指定すると、download.example.com にも送信可能なCookieを発行できる。これは両者が同じ親ドメインを共有している場合の設計だ。
Copied!!Set-Cookie: CloudFront-Policy=...; Domain=example.com; Path=/files/; Secure; HttpOnly; SameSite=Lax
Domain を省略すると通常は発行元ホスト専用になり、download.example.com には送信されない。api.example.com から無関係な d123.cloudfront.net 向けのCookieを設定することもできない。
フロントエンドとAPIのオリジンが異なる場合、ブラウザ側の fetch(..., { credentials: 'include' }) やAPIの適切なCORS設定も必要になる。Set-Cookie はJavaScriptから直接読み取るヘッダーではない。
親ドメイン共有Cookieは対象サブドメインに送信され得るため、サブドメインの管理とHTTPSの徹底も重要だ。
5. Cookieの有効期限とCloudFrontの許可期限は別
Max-Age や Expires はブラウザ側のCookie保持期間。一方、ポリシーの DateLessThan はCloudFrontが検証する許可期限だ。
たとえブラウザがCookieを保持していても、署名付きポリシーの期限が切れればCloudFrontは拒否する。逆に、ブラウザがCookieを失えばポリシーの期限前でもアクセスできない。
短時間のアクセス許可が必要なら、ポリシーの有効期限を短くする。ただし、CloudFrontは各HTTPリクエスト時に期限を判定するため、期限内に開始した大きなダウンロードが途中で必ず停止するわけではない。期限切れ後の再リクエストは拒否される。
6. ロール別のアクセス制御をどう表現するか
例えば次の要件を考える。
| ロール | アクセス可能なURL |
|---|---|
| 一般ユーザー | /files/public/* |
| チームA | /files/team-a/* |
| 管理者 | /files/admin/* |
APIはログイン済みユーザーのロールを確認し、実際に許可されたURLパターンだけを Resource に含めて署名する。
ただし、ここに大切な制約がある。CloudFront署名付きCookieのカスタムポリシーは、任意個数の独立したURL許可リストを表現する汎用RBACエンジンではない。 典型的には1つのリソースパターンを対象に設計する。複数の離れたパスへの許可や、オブジェクトごとの細かなACLが必要なら、パス設計を見直すか、署名付きURLや追加の認可レイヤーを検討する。
また、CloudFrontが見ているのはリクエストURLであり、アプリケーションDB上のファイルIDやロールではない。URLのパスと認可対象を整合させる必要がある。
7. 失効・ロール変更に関する落とし穴
署名付きCookieは、発行後にAPIへ毎回問い合わせる仕組みではない。したがって、ユーザーのロールを変更しても、発行済みのCookieはポリシーの期限まで有効であり得る。
対策の候補は次の通り。
- ポリシーの有効期限を短くする。
- 更新時にAPIが最新ロールを再検証する。
- 即時失効が必須なら、CloudFront Functions / Lambda@Edge 等を含む別の認可方式を検討する。
- 秘密鍵の保護・ローテーションを設計する。ただし鍵の切り替えを通常の個別ユーザー失効手段として扱わない。
ブラウザからCookieを削除するログアウト処理だけでは、既にコピーされた署名付きCookieをサーバー側で個別に失効させることはできない。これは通常のサーバー側セッションとの大きな違いだ。
8. S3を非公開にする
S3バケットは公開しない。CloudFrontの Origin Access Control(OAC) を使い、対象ディストリビューションからの読み取りのみをバケットポリシーで許可する。
ここで区別したいのは、CloudFront署名付きCookieは閲覧者→CloudFrontの認可、OACはCloudFront→S3のアクセス制御という点。両方が必要になる。
また、制限したいファイルパスがCloudFrontの適切なキャッシュビヘイビアに一致し、信頼されたキーグループが設定されていることを確認する。意図しない公開ビヘイビアが残っていないかも検証する。
9. 実装・検証チェックリスト
- APIは認証済みユーザーのロールとファイル権限をサーバー側で検証する
- 許可対象URLとS3キーの対応が明確になっている
- CloudFrontのキーグループと公開鍵を設定している
- 秘密鍵はSecrets Managerなどで適切に保護する
- 署名付きCookieの
Domain、Path、Secure、HttpOnly、SameSiteを確認する - APIとブラウザが別オリジンの場合はCORSとcredentialsを確認する
- ポリシー期限切れ、改ざん、範囲外パスへのアクセスが403になることを確認する
- S3の直接公開を禁止し、OAC経由だけに制限する
- ロール変更・ログアウト時の失効要件を明文化する
- 署名付きURLと署名付きCookieを同じファイルに併用する場合の優先関係を確認する
10. まとめ
この構成の利点は、認可判断はAPI、配信はCloudFrontと責務を分離できること。APIサーバーが大容量ファイルを中継する必要がなく、CloudFrontのキャッシュも活用できる。
一方で、CloudFront署名付きCookieは万能なロール認可基盤ではない。URLの設計、短い有効期限、ロール変更時の失効要件、Cookieのドメイン制約を考慮して初めて安全に運用できる。
