executor 07(全コードベースのコードレビュー)が発見し、独立したセキュリティレビュー(サブエージェント, opus-4.7)で裏付けた。
問題
apps/cdk/lib/constructs/webapp.ts は Docker ビルド引数 ALLOWED_ORIGIN_HOST を、カスタムドメイン分岐では *.${hostedZone.zoneName} に(CloudFront デフォルト分岐では *.cloudfront.net に)設定している。その値は apps/webapp/next.config.ts に取り込まれ、experimental.serverActions.allowedOrigins に渡される。
serverActions.allowedOrigins は CORS リストではなく、Next.js のデフォルトの CSRF 保護(Origin/Host の一致)を抑制する許可リストである。ワイルドカードを追加すると、そのホストゾーンの任意の兄弟サブドメイン(アプリが所有していない、新規登録された、乗っ取られた、他テナントと共有されているサブドメインを含む)が同一アプリのオリジンとして扱われ、認証済みの Server Actions をアプリに対して送信できてしまう。兄弟サブドメインは same-site であるため、SameSite=Lax クッキーではこれを防げない。
*.cloudfront.net のフォールバックはさらに悪く、インターネット上の すべての CloudFront ディストリビューションを信頼された CSRF オリジンにしてしまう。
一次情報: https://nextjs.org/docs/app/api-reference/config/next-config-js/serverActions (Server Actions allowedOrigins / 組み込みの CSRF 保護)。
根拠
apps/cdk/lib/constructs/webapp.ts:80 — ALLOWED_ORIGIN_HOST: hostedZone ? \.${hostedZone.zoneName}` : '.cloudfront.net'`
apps/webapp/next.config.ts:3-18 — ビルド引数から allowedOrigins を構築。
対応の方向性(要議論)
- カスタムドメイン分岐: 具体的なホスト(
${subDomain}.${hostedZone.zoneName}、現状は web.${zone})を渡し、ブラウザの Origin と一致させる。
- CloudFront デフォルト分岐: ドメインはデプロイ時に判明し、Lambda ↔ CloudFront の循環依存を生む(コード内で既に文書化済み)ため、「正確なホストを渡すだけ」では不十分。検討する選択肢: (a) デフォルトの
same-origin 挙動を受け入れ、ビルド引数を完全に削除する(同一ホストから送信される Server Actions は Origin/Host 一致で成功する); (b) AMPLIFY_APP_ORIGIN_SOURCE_PARAMETER を既に更新している既存の AwsCustomResource を通じて、実行時に ALLOWED_ORIGIN_HOST を更新する。
この修正はエンドツーエンド(Lambda Function URL のレスポンスストリーミング → Next.js Server Actions を通じた x-forwarded-host の伝播)で検証する必要があるため、盲目的にマージするのではなく議論用の issue として起票する。
サンプルとしての教育的リスク
このサンプルは、allowedOrigins をあたかも寛容な CORS リストであるかのように設定することを新規ユーザーに教えてしまう。コピーしたアプリが beta.${zone} や実験的なサブドメインを追加すると、同一ホストゾーンの任意のサブドメインに対する暗黙の信頼を引き継ぐ。
関連
executor 07(全コードベースのコードレビュー)が発見し、独立したセキュリティレビュー(サブエージェント, opus-4.7)で裏付けた。
問題
apps/cdk/lib/constructs/webapp.tsは Docker ビルド引数ALLOWED_ORIGIN_HOSTを、カスタムドメイン分岐では*.${hostedZone.zoneName}に(CloudFront デフォルト分岐では*.cloudfront.netに)設定している。その値はapps/webapp/next.config.tsに取り込まれ、experimental.serverActions.allowedOriginsに渡される。serverActions.allowedOriginsは CORS リストではなく、Next.js のデフォルトの CSRF 保護(Origin/Host の一致)を抑制する許可リストである。ワイルドカードを追加すると、そのホストゾーンの任意の兄弟サブドメイン(アプリが所有していない、新規登録された、乗っ取られた、他テナントと共有されているサブドメインを含む)が同一アプリのオリジンとして扱われ、認証済みの Server Actions をアプリに対して送信できてしまう。兄弟サブドメインは same-site であるため、SameSite=Laxクッキーではこれを防げない。*.cloudfront.netのフォールバックはさらに悪く、インターネット上の すべての CloudFront ディストリビューションを信頼された CSRF オリジンにしてしまう。一次情報: https://nextjs.org/docs/app/api-reference/config/next-config-js/serverActions (Server Actions allowedOrigins / 組み込みの CSRF 保護)。
根拠
apps/cdk/lib/constructs/webapp.ts:80—ALLOWED_ORIGIN_HOST: hostedZone ? \.${hostedZone.zoneName}` : '.cloudfront.net'`apps/webapp/next.config.ts:3-18— ビルド引数からallowedOriginsを構築。対応の方向性(要議論)
${subDomain}.${hostedZone.zoneName}、現状はweb.${zone})を渡し、ブラウザの Origin と一致させる。same-origin挙動を受け入れ、ビルド引数を完全に削除する(同一ホストから送信される Server Actions は Origin/Host 一致で成功する); (b)AMPLIFY_APP_ORIGIN_SOURCE_PARAMETERを既に更新している既存のAwsCustomResourceを通じて、実行時にALLOWED_ORIGIN_HOSTを更新する。この修正はエンドツーエンド(Lambda Function URL のレスポンスストリーミング → Next.js Server Actions を通じた
x-forwarded-hostの伝播)で検証する必要があるため、盲目的にマージするのではなく議論用の issue として起票する。サンプルとしての教育的リスク
このサンプルは、
allowedOriginsをあたかも寛容な CORS リストであるかのように設定することを新規ユーザーに教えてしまう。コピーしたアプリがbeta.${zone}や実験的なサブドメインを追加すると、同一ホストゾーンの任意のサブドメインに対する暗黙の信頼を引き継ぐ。関連
.kiro/specs/v3-release-prep/review-full-report.md(H2)