目次
はじめに
こんにちは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それぞれの対応ですと、そこまでハードルは高くないように
思われますが、一緒に実装すると苦労がありました。
最後までお読みいただきありがとうございます。
それでは、今回はこの辺で。
ディーネット構築担当のエンジニアです。