2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CloudFront + Lambda関数URL 構成のPOST/PUT リクエストにおけるセキュリティ対応

Last updated at Posted at 2025-04-25

はじめに

バックエンドにLambda関数URLを用いる場合のセキュリティについて考える機会がありました。
この場合、Lambda関数URLの認証タイプをNONEに設定すると、誰でも直接Lambda関数にアクセスできてしまいセキュリティ的によろしくありません。

そこで今回の記事では、CloudFrontにLambda関数URLオリジンへのアクセスを制限するためのオリジンアクセスコントロール (OAC) が用意されていますので、それを利用してセキュリティ担保する方法を検証してみました。

POST/PUTリクエストの場合は一工夫必要でしたので、その紹介もさせていただければと思います。

環境構成

Nuxt3 SSR
CloudFront(オリジンはS3とLambda関数URL)
S3
Lambda関数URL

前提条件

OAC を作成して設定する前に、Lambda 関数 URL をオリジンとして持つ CloudFront ディストリビューションが必要ですので、既にこれらのリソースが存在している事を前提とします。

やること

  1. Lambda関数URLにIAM認証を設定する
  2. Lambda関数URL へのアクセス許可を OAC に付与する
  3. OACが採用しているSigV4認証のためにリクエストボディのSHA-256ハッシュ値がx-amz-content-sha256ヘッダーに付与されるようにする

1. Lambda関数URLにIAM認証を設定する

OAC を使用するには、AWS_IAM を指定する必要があります。

以下スクショの通り、Lambda関数の認証タイプをAWS_IAMに更新します。

スクリーンショット 2025-04-10 3.25.39.png

2. Lambda 関数 URL へのアクセス許可を OAC に付与する

CloudFront サービスプリンシパル (cloudfront.amazonaws.com) に Lambda 関数 URL へのアクセスを許可します。

OACを作成し、CloudFrontのオリジンにOACを設定し、表示されたコマンドをコピーしつつこのオリジン設定を保存します。
スクリーンショット 2025-04-10 3.27.06.png

コピーしたコマンドの関数名部分を入れ込んだ上でコマンド実行すると、Lambda関数にポリシーが追加されます。

スクリーンショット 2025-04-10 3.27.52.png

3. OACが採用しているSigV4認証のためにリクエストボディのSHA-256ハッシュ値がx-amz-content-sha256ヘッダーに付与されるようにする

2で終わりかと思いきや、POST/PUTリクエストにおいては、リクエスト本文のSHA256を計算し、本文のペイロードハッシュ値をx-amz-content-sha256ヘッダーに含めた上で、オリジンにリクエスト送信しないとLambda関数のIAM認証を突破できないようでした。

上記公式に以下記述がありました。

スクリーンショット 2025-04-10 4.07.18.png

OAC認証の仕組み

調べてみると、OAC認証の仕組みは以下であることがわかりました。

一方向性:ハッシュ値から元のデータを復元することは実質的に不可能
衝突耐性:異なる入力から同じハッシュ値が生成される確率は極めて低い
雪崩効果:入力データのわずかな変更でも、出力ハッシュ値は大きく変わる

そこで、以下を参考にさせていただき、実際に対応してみました。

簡単に言うと、
Lambda@Edgeではbodyのハッシュ値だけ計算し、SigV4はOACで署名する
というアプローチになります。

Lambda関数を作成

Lambda@Edge関数を作成するには、バージニア北部(us-east-1)リージョンに設定されている必要がありますのでご注意ください。

また、arm64アーキテクチャで関数を作成すると、Lambda@Edgeのデプロイの際に非対応のエラーが出ますので、x86_64で作成します。

スクリーンショット 2025-04-10 3.20.25.png

アクセス権限に関しては以下のように設定します。

スクリーンショット 2025-04-10 3.21.20.png

関数コードを編集

以下のように編集し、Deployを押下します。

スクリーンショット 2025-04-10 3.23.00.png

こちらにもコードを記載しておきます。

index.mjs
const hashPayload = async (payload) => {
  const encoder = new TextEncoder().encode(payload);
  const hash = await crypto.subtle.digest("SHA-256", encoder);
  const hashArray = Array.from(new Uint8Array(hash));
  return hashArray.map((bytes) => bytes.toString(16).padStart(2, "0")).join("");
};

export const handler = async (
  event,
  _context,
) => {
  const request = event.Records[0].cf.request;
  console.log("originalRequest", JSON.stringify(request));

  if (!request.body?.data) {
    return request;
  }

  const body = request.body.data;
  // CloudFront経由でLambda@Edgeに渡されるリクエストは、Base64エンコードされた文字列としてボディデータが含まれる。
  const decodedBody = Buffer.from(body, "base64").toString("utf-8");

  request.headers["x-amz-content-sha256"] = [
    { key: "x-amz-content-sha256", value: await hashPayload(decodedBody) },
  ];
  console.log("modifiedRequest", JSON.stringify(request));

  return request;
};

Lambda@Edgeデプロイ

トリガーを追加を押下します。

スクリーンショット 2025-04-10 3.23.39.png

トリガーの設定にてCloudFrontを選択し、Lambda@Edgeへのデプロイを押下します。

スクリーンショット 2025-04-10 3.23.52.png

以下スクショの通りに選択し、デプロイします。

スクリーンショット 2025-04-10 3.24.20.png

CloudFrontのビヘイビアを確認すると、Lambda@Edgeが登録されていることが確認できます。
これで、Lambdaオリジンへのリクエストの際にLambda@Edgeが実行される形となります。

スクリーンショット 2025-04-10 3.28.34.png

動作確認

ブラウザからLambda関数が実行されるページにアクセスすると、
Chromeの開発者ツールのネットワークより、200が確認できました。
スクリーンショット 2025-04-10 3.57.04.png

また、curlで実行すると403が返ってきました。
スクリーンショット 2025-04-10 3.15.49.png

これにより、CloudFront経由でのLambda実行のみ許可されている事が確認できました。

まとめ

OACで対応できるのとても良いですね。
IaC版の記事も書かないと..

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?