ものづくりのブログ

うちのネコを題材にしたものづくりができたらいいなと思っていろいろ奮闘してます。

【api】特定のホストのみリクエストを実行させたい

Lambda + API Gateway の構成で、特定のホスト(ヘッダーの値)以外のリクエストを 500 Internal Server Error で返す処理を検討します。

方法

Lambda 関数内で Host ヘッダーをチェック

API Gateway のリクエスト情報 (event) から headers["X-Forwarded-For"] を取得し、許可されたホストかどうかを判定します。

許可されていないホストなら 500 エラーを返す

許可リストにない場合、500 の HTTP ステータスコードを返します。

X-Forwarded-For ヘッダーとは?

X-Forwarded-For は、プロキシやロードバランサを経由したリクエストの元のクライアント IP アドレスを取得するための HTTP ヘッダーです。
通常、クライアントが直接サーバーにリクエストすると、サーバーは REMOTE_ADDR などで直接クライアントの IP アドレスを取得できます。しかし、リクエストが プロキシ や ロードバランサ を経由すると、サーバー側からはプロキシの IP アドレスしか見えません。
そこで、プロキシやロードバランサは X-Forwarded-For ヘッダーを追加し、元のクライアント IP を記録します。

  • 一番左のIPが「元のクライアントのIP」
  • 右側に行くほど、経由したプロキシやロードバランサの IP が追加される
X-Forwarded-For: 203.0.113.5, 192.168.1.1, 10.0.0.1
  • 203.0.113.5 → 元のクライアント
  • 192.168.1.1 → 中継プロキシ
  • 10.0.0.1 → 最後のプロキシ(API Gateway など)

実装例(X-Forwarded-Forで制限)

ALLOWED_IPS = {"203.0.113.5"}

def lambda_handler(event, context):
    headers = event.get("headers", {})
    x_forwarded_for = headers.get("X-Forwarded-For", "")
    client_ip = x_forwarded_for.split(",")[0] if x_forwarded_for else "Unknown"

    if client_ip not in ALLOWED_IPS:
        return {
            "statusCode": 403,
            "body": "Forbidden"
        }

    return {
        "statusCode": 200,
        "body": f"Access granted for {client_ip}"
    }

注意点

偽装の可能性がある
  • X-Forwarded-For はクライアントが自由に書き換えられるため、悪意のあるユーザーが偽の IP を設定することができます。
  • AWS の ALB や CloudFront などの信頼できるプロキシを通す場合は、信頼できるリスト (trusted proxies) を考慮して処理するとよいです。

referer ヘッダーとは?

Referer(リファラー)ヘッダーは、現在のリクエストがどこから来たのか を示す HTTP ヘッダーの一つです。
例えば、あるウェブページ(https://example.com)のリンクをクリックして別のページ(https://target.com)に移動した場合、https://target.com へのリクエストには以下のような Referer ヘッダーが含まれます。

Referer: https://example.com

Referer ヘッダーの用途

  • セキュリティ対策
    • 許可されたページ(ドメイン)のみアクセスを許可する
    • CSRF(クロスサイトリクエストフォージェリ)対策
  • アナリティクス
    • どのサイトからの流入が多いか分析
    • マーケティング効果測定
  • 画像やリソースのホットリンク防止
  • 他のサイトからの無断直リンクを防ぐ

Referer ヘッダーの注意点

  • ブラウザや設定によって送信されない場合がある
    • セキュリティ設定によって Referer が削除されることがある
    • HTTPS → HTTP の遷移では送信されない(no-referrer-when-downgrade)
  • スペルミスに注意("Referer" ではなく "Referrer")
    • Referer は「Referrer(リファラー)」のスペルミスが標準になっている
  • Referrer-Policy による影響
    • Referrer-Policy HTTP ヘッダーの設定により Referer の送信内容が制御される
    • 例: no-referrer にすると Referer は送信されない

実装例(refererで制限)

Referer ヘッダーを使って、特定のサイトからのリクエストのみ許可するように制限できます。

方法

  • 環境変数 (ENV) で本番環境かどうかを判定
  • 許可する Referer リスト (ALLOWED_REFERRERS) を設定
  • Referer ヘッダーの値をチェック
  • 許可リストにない場合は 403 Forbidden を返す
  • 開発環境 (ENV=development) では制限なし

実装例

import json
import os

# 許可する Referer のリスト(ドメインのみ指定)
ALLOWED_REFERRERS = {
    "https://example.com",
    "https://sub.example.com"
}

# 環境変数で環境を判定(デフォルトは 'development')
ENV = os.getenv("ENV", "development")

def lambda_handler(event, context):
    headers = event.get("headers", {})
    referer = headers.get("Referer", "")

    # 本番環境のみ Referer 制限を適用
    if ENV == "production" and not any(referer.startswith(r) for r in ALLOWED_REFERRERS):
        return {
            "statusCode": 403,
            "body": json.dumps({"message": "Forbidden: Invalid Referer"}),
        }

    return {
        "statusCode": 200,
        "body": json.dumps({"message": f"Access granted for Referer: {referer}"}),
    }

ポイント

  • Referer ヘッダーは省略されることがある
    • ブラウザの設定やセキュリティ機能で Referer が送信されないことがあるため、Referer が空でも許可するか検討する。
  • startswith() を使って Referer をチェック
  • CORS 設定と併用可能
    • Referer 制限と CORS の Access-Control-Allow-Origin 設定を組み合わせると、より強力なセキュリティ対策になる