AWS CloudFrontとLambdaを組み合わせたAPIへのアクセス制限方法
本記事は、GitHubで公開しているアプリケーション「ai-mon」のバックエンドAPI(APIGateway + Lambda)に、CloudFront経由でのアクセスのみを許可するための仕組みを実装した際の技術的なメモです。具体的には、カスタムヘッダーを使ったアクセス制限について解説します。
下記の記事を参考にsamコマンドで実行する方法を整理しました。
なぜCloudFrontからのアクセスのみに制限するのか?
多くのWebアプリケーションでは、コンテンツ配信を最適化するためにCloudFront(CDN: Content Delivery Network)を導入します。これにより、ユーザーは高速なアクセスが可能になります。このアプリケーションもCloudFrontを介してAPIGatewayとLambdaで構成されたAPIにアクセスする設計です。
しかし、CloudFrontを経由させても、APIGatewayのエンドポイントURLは外部から直接アクセス可能です。エンドポイント自体は複雑な文字列ですが、アプリケーションのソースコードが公開されている場合、そのURLを知られてしまうリスクがあります。
別の認証機能がありますが、そちらはDBなどへのアクセスがあり不要なアクセスによって必要以上のリソースを消費することにつながります。
そうした必要以上のリソースの消費を防ぐための仕組みが必要になります。
どのようなアクセス制限を行なったか
今回は、CloudFrontからAPIGatewayにリクエストを送る際に、特定のカスタムヘッダーを付与し、Lambda側でそのヘッダーの値が正しいかを検証する手法を採用しました。
これにより、カスタムヘッダーを持たない直接的なアクセスをLambdaで拒否し、不要なリソース消費を防ぐことができます。この手法は、APIGatewayのAPIキーやリソースポリシーだけでは実現できない、より厳密なアクセス制限ができます。
どう構築するか
構築には、GitHub ActionsによるCI/CDとAWS SAM(Serverless Application Model)を利用しました。これにより、カスタムヘッダーのシークレット値を安全に管理し、デプロイプロセスを自動化できます。
さらにCloudFrontからのカスタムヘッダーの存在有無だけではなく、値自体も比較しチェックするようにする必要があります。さらにその値を秘匿化することと値のずれを発生しないようにすることが必要になります。
概念図はこちらです。
samコマンド&yamlファイルの記述
値はGithubのシークレットから取得して反映するので値を秘匿されます。
GithubActionの値の受け渡し
以下のコードで値を受け渡します。
CLOUD_FRONT_API_SECRET_VALUEで取得した値を、CLOUD_FRONT_API_SECRET_VALUEの変数に変更して受け渡しています。
- name: Deploy SAM Application
env:
CLOUD_FRONT_API_SECRET_VALUE: ${{ secrets.CLOUD_FRONT_API_SECRET_VALUE }}
run: sam deploy --region ap-northeast-1 --stack-name スタック名 --no-confirm-changeset --no-fail-on-empty-changeset --config-file ./backend/src/lambda/samconfig.toml --parameter-overrides "CloudFrontSecretHeaderValue=${CLOUD_FRONT_API_SECRET_VALUE}"
Globals:
Function:
Timeout: 30
Runtime: nodejs22.x
LoggingConfig:
LogFormat: JSON
Environment:
Variables:
CLOUD_FRONT_API_SECRET_VALUE: !Ref CloudFrontSecretHeaderValue
Parameters:
CloudFrontSecretHeaderValue:
Type: String
Description: "Secret value for CloudFront custom header"
------------------------------------------------------------------------
Cloudfront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- Id: ApiGatewayOrigin
DomainName: !Sub '${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com'
OriginCustomHeaders:
- HeaderName: X-CloudFront-Secret
HeaderValue: !Ref CloudFrontSecretHeaderValue
LambdaはGlobalsで共通の変数として定義して、全ての関数に値を分配しました。
CloudFrontの確認
オリジンの設定の中に以下のようなカスタムヘッダーの設定があれば正しく連携できています。

Lambdaの設定
Lambda側の環境変数に設定されていれば正しく連携できています。
Lambdaの実装
実装の一部を抜粋しました。以下の実装で値のチェックを行いました。
/**
* CloudFrontからのカスタムヘッダーと��境変数の値を比較してリクエストを検証します。
* @param {object} headers - リクエストヘッダー
* @returns {boolean} - ヘッダーの値が環境変数の値と一致する場合はtrue、それ以外はfalse
*/
export const validateCloudFrontSecret = (headers) => {
const secretFromHeader = headers['X-CloudFront-Secret'];
const secretFromEnv = process.env.CLOUD_FRONT_API_SECRET_VALUE;
if (!secretFromHeader || !secretFromEnv) {
console.error("Secret value is missing in header or environment variable.");
return false;
}
return secretFromHeader === secretFromEnv;
};
まとめ
今回はAPIGatewayのアクセス制御を行うことで、不正なアクセスを防止する環境の構築をまとめました。

