最終更新日:2024-09-03 17:00:38
この例では、Edge Cloud Apps関数とWeb Crypto APIを使用して、特定のWebサイトリソースへのアクセスを保護する方法を示します。実例として、http://ecatest.cdnetworks.com/ECA-test/pet-shop-website-template/about.html
にあるペットショップの「会社概要」ページを使用します。このページにアクセスするには、HMACおよびSHA-256アルゴリズムを使用して生成された有効な署名が必要です。Edge Cloud Apps関数は、クライアントに代わって送信リクエストに署名したり、受信した署名付きリクエストを検証したりして、セキュリティゲートキーパーとして機能できます。権限のないリクエストは、エラーページに遭遇します。
const SECRET_KEY = "my secret symmetric key";
// リクエストを処理し、リクエストメソッドに基づいて署名または検証を行います
async function handleRequest(request) {
if (request.method === "POST") {
return signRequest(request);
} else {
return verifyRequest(request);
}
}
// リクエスト署名を検証します
async function verifyRequest(request) {
const url = new URL(request.url);
// リクエストに必要なクエリパラメータが含まれていることを確認します
if (!url.searchParams.has("mac") || !url.searchParams.has("expiry")) {
return failPage("クエリパラメータがありません");
}
const encoder = new TextEncoder();
const secretKeyData = encoder.encode(SECRET_KEY);
// 署名検証のためにキーをインポートします
const key = await crypto.subtle.importKey(
"raw",
secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"],
);
// 検証が必要なデータの抽出:パスと有効期限のタイムスタンプ
const expiry = Number(url.searchParams.get("expiry"));
const dataToAuthenticate = url.pathname + expiry;
// 受信したBase64エンコードされたMACをUint8Arrayに変換します
const receivedMacBase64 = url.searchParams.get("mac");
const receivedMac = byteStringToUint8Array(atob(receivedMacBase64));
// タイミング攻撃を防ぐためにcrypto.subtle.verify()を使用して署名を検証します
const verified = await crypto.subtle.verify(
"HMAC",
key,
receivedMac,
encoder.encode(dataToAuthenticate),
);
// 検証に失敗した場合は、エラーページを返します
if (!verified) {
return failPage("無効なMAC");
}
// URLの有効期限が切れているかどうかを確認します
if (Date.now() > expiry) {
return failPage(`${new Date(expiry)}にURLの有効期限が切れました`);
}
// 検証に成功しました。リソースへのアクセスを許可します
return fetch(request);
}
// ByteStringをUint8Arrayに変換します
function byteStringToUint8Array(byteString) {
const ui = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; ++i) {
ui[i] = byteString.charCodeAt(i);
}
return ui;
}
// リクエストに署名します
async function signRequest(request) {
const url = new URL(request.url);
return generateSignedUrl(url);
}
// 署名付きURLを生成します
async function generateSignedUrl(url) {
// 署名生成のためにキーをインポートします
const encoder = new TextEncoder();
const secretKeyData = encoder.encode(SECRET_KEY);
const key = await crypto.subtle.importKey(
"raw",
secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
// URLの有効期限を設定します
const expirationMs = 18000; // 18秒
const expiry = Date.now() + expirationMs;
const dataToAuthenticate = url.pathname + expiry;
// 署名を生成します
const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(dataToAuthenticate));
// 署名をBase64エンコードされた文字列に変換します
const base64Mac = btoa(String.fromCharCode(...new Uint8Array(mac)));
// 署名と有効期限をURLクエリパラメータに追加します
url.searchParams.set("mac", base64Mac);
url.searchParams.set("expiry", expiry);
return new Response(url);
}
// エラーページを生成します
function failPage(msg) {
let page = FAILPAGE.replace('{{error-msg}}', msg);
return new Response(page, {headers: {"Content-Type": "text/html; charset=utf-8"}});
}
// エラーページのHTMLテンプレート
const FAILPAGE = `
<!DOCTYPE html>
<html>
<head>
<title>エラーページ</title>
<style>
// ... ページスタイル ...
</style>
</head>
<body>
<div class="container">
<h1>エラー</h1>
<p>アクセスしようとしているURLは正しく署名されていません: {{error-msg}}</p>
<div class="url-section">
<div class="url-wrapper">
<p><span class="url-display" id="current-url" style="display: block;"></span></p>
</div>
<div class="button-wrapper">
<button class="url-button" type="button" onclick="getSignedUrl()">URLに署名する</button>
</div>
<div class="url-wrapper" id="signed-url-section">
<p><strong>署名付きURLは18秒後に期限切れになります</strong></p>
<p><span class="url-display" id="signed-url"></span></p>
</div>
</div>
</div>
<script>
// ... ページスクリプト ...
</script>
</body>
</html>
`;
addEventListener("fetch", event => {
return event.respondWith(handleRequest(event.request));
});
Edge Cloud Apps関数は、Web Crypto APIを完全にサポートしており、エッジでの暗号化と復号化のニーズを満たすことができます。Edge Cloud Apps関数を使用して、クライアントに代わってリクエストに署名したり、受信した署名付きリクエストを検証したりすることで、不正アクセス(ホットリンク保護)を効果的に防止できます。