[アドカレ2025]CloudFront-FunctionでCORSの許可かつBasic認証を実装する

はじめに

こんにちはyanaiです。
寒いですね。
今の時期外出するときは腹巻をしてるんですが、
出社するときだけなぜか見つからず、泣く泣く無防備な状態で出社してます。

さて、本題ですが
CloudFront+S3でホスティングしているサイトのURLに
CORSの許可を設け、なおかつ許可したURL以外からは
Basic認証を設定する検証をした際に
設定に苦労したので、書き起こそうと思います。

図にするとこんな感じです。

前提

EC2でapacheを導入済み。ドメインは手順内ではfuga.comとします。
CloudFront+S3は構築済み。ドメインは手順内ではhoge.comとします。

手順

以下検証していったこと記載します。

手順1 EC2側の設定

EC2側にCORSのチェック用htmlファイルを置きます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CORS Test</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        .container {
            text-align: center;
            padding: 40px;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        #result {
            font-size: 24px;
            font-weight: bold;
            margin-top: 20px;
            padding: 20px;
            border-radius: 4px;
        }
        .success {
            color: #28a745;
            background-color: #d4edda;
        }
        .error {
            color: #dc3545;
            background-color: #f8d7da;
        }
        input {
            width: 400px;
            padding: 10px;
            font-size: 14px;
            margin: 10px 0;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
        }
        button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>CORS Test</h1>
        <input type="text" id="testUrl" placeholder="テストするURLを入力してください">
        <br>
        <button onclick="testCors()">CORS テスト実行</button>
        <div id="result"></div>
    </div>
    <script>
        function testCors() {
            const resultDiv = document.getElementById('result');
            const testUrl = document.getElementById('testUrl').value;

            if (!testUrl) {
                resultDiv.textContent = 'URLを入力してください';
                resultDiv.className = 'error';
                return;
            }

            resultDiv.textContent = 'テスト中...';
            resultDiv.className = '';

            fetch(testUrl, {
                method: 'GET',
                mode: 'cors'
            })
            .then(response => {
                if (response.ok || response.type === 'cors') {
                    resultDiv.textContent = 'corsok';
                    resultDiv.className = 'success';
                } else {
                    resultDiv.textContent = 'CORS エラー: ' + response.status;
                    resultDiv.className = 'error';
                }
            })
            .catch(error => {
                resultDiv.textContent = 'CORS エラー: ' + error.message;
                resultDiv.className = 'error';
            });
        }
    </script>
</body>
</html>

ブラウザからアクセスすると、以下のような表示のサイトになります。

現状Cloudfront+S3側にCORSの許可設定を入れてないので、https://hoge.comと入力してもエラーとなります。

手順2 CFの設定

CloudFrontのキャッシュでカスタムポリシーを作成し
キャッシュキーの
ヘッダーに以下を追加します。

Origin
Access-Control-Request-Method
Access-Control-Request-Headers

また、ビヘイビアの設定の
許可された HTTP メソッド
GET, HEAD, OPTIONSを選択します。

ここまで完了したら、
CloudFront-functionを利用して、
Basic認証とCORSのアクセスを許可する関数を作成します。
関数のコードは以下を設定しました。

function handler(event) {
    var request = event.request;
    var headers = request.headers;
    var method = request.method;

    var origin = headers.origin ? headers.origin.value : null;

    var allowedOrigins = [
        "https://fuga.com",
    ];

    var responseHeaders = {
        "access-control-allow-methods": { value: "GET, HEAD, OPTIONS" },
        "access-control-allow-headers": { value: "*" }
    };

    // オリジンが許可リストに含まれている場合だけ付与
    if (origin && allowedOrigins.includes(origin)) {
        responseHeaders["access-control-allow-origin"] = { value: origin };
    }

    if (method === "OPTIONS") {
        return {
            statusCode: 204,
            statusDescription: "No Content",
            headers: responseHeaders
        };
    }

    // 認証チェック
    var authString = "Basic base64EncodedString"; // Base64 encode
    if (
        typeof headers.authorization === "undefined" ||
        headers.authorization.value !== authString
    ) {
        return {
            statusCode: 401,
            statusDescription: "Unauthorized",
            headers: Object.assign({
                "www-authenticate": { value: 'Basic realm="Restricted Area"' }
            }, responseHeaders)
        };
    }

    // 認証通過 → 通す
    return request;
}

ご利用いただく場合は
allowedOriginsで設定しているCORSのURLと
basic認証用のauthStringの記述を変更ください。

作成した関数はビューワーリクエストに設定します。

ポイントとしては、
ブラウザが送るクロスオリジンのGETやPOSTなどの実際のリクエストを送る前に
確認用に送るCORSプリフライト(OPTIONS)には、
通常Authorizationヘッダーが含まれないようで、
CloudFrontがBasic認証を要求した際はエラーになってしまいます。
なので、本コードでは許可されたOriginのURLでCORSプリフライト(OPTIONS)を
送ってきたものは204を返すように設定してます。

こちらを実装した後
corsのチェック用htmlで確認するとCORSのテストが通るようになることが確認できます。

また、ブラウザでCloudFront側のURL(https://hoge.com)にアクセスした際はBasic認証も実装されておりました。

最後に

CloudFrontにてBasic認証とCORSの許可を設定する方法を書かせていただきました。
Basic認証とCORSそれぞれの対応ですと、そこまでハードルは高くないように
思われますが、一緒に実装すると苦労がありました。
最後までお読みいただきありがとうございます。
それでは、今回はこの辺で。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA