コンテンツにスキップ

リクエストに署名する

HMACおよびSHA-256アルゴリズムを使用して署名されたリクエストを検証するか、403を返します。

次のスニペットは以下のことを行います:

  • /generate/で始まるリクエストURLの場合、/generate//に置き換え、結果のパスにタイムスタンプを付加して署名し、レスポンスボディに完全な署名付きURLを返します。

  • その他のすべてのリクエストURLについては、署名されたURLを検証し、リクエストを通過させます。

export default {
async fetch(request) {
const secretKey = "your_secret_key"; // 実際の秘密鍵に置き換えてください
const expiration = 60; // 有効期限(HMACトークンが有効であるべき時間、秒単位)
const encoder = new TextEncoder();
// HMAC-SHA256署名用の秘密鍵をインポート
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secretKey),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign", "verify"],
);
const url = new URL(request.url);
// リクエストURLが/generate/で始まるか確認
if (url.pathname.startsWith("/generate/")) {
// /generate/を/に置き換え
url.pathname = url.pathname.replace("/generate/", "/");
const currentTimestamp = Math.floor(Date.now() / 1000); // 現在のタイムスタンプ(秒単位)
// 認証するデータ:パス名とタイムスタンプを結合
const dataToAuthenticate = `${url.pathname}${currentTimestamp}`;
// HMAC-SHA256でデータに署名
const signature = await crypto.subtle.sign(
"HMAC",
key,
encoder.encode(dataToAuthenticate),
);
// タイムスタンプとHMACを安全にエンコード
const signatureBase64 = btoa(
String.fromCharCode(...new Uint8Array(signature)),
);
const signedData = `${currentTimestamp}-${signatureBase64}`;
const encodedSignedData = encodeURIComponent(signedData);
// 署名付きURLを作成
const signedURL = `${url}?verify=${encodedSignedData}`;
// レスポンスボディに署名付きURLを返す
return new Response(signedURL, { status: 200 });
}
// その他のすべてのリクエストURLについて、署名されたURLを検証
const params = new URLSearchParams(url.search);
const verifyParam = params.get("verify");
if (!verifyParam) {
return new Response("検証パラメータが欠落しています", { status: 403 });
}
// 検証パラメータをデコードし、タイムスタンプとHMACに分割
const decodedVerifyParam = decodeURIComponent(verifyParam);
const [timestampStr, receivedMac] = decodedVerifyParam.split("-");
// タイムスタンプを解析し、有効な数値であることを確認
const timestamp = parseInt(timestampStr, 10);
if (isNaN(timestamp)) {
return new Response("無効なタイムスタンプ", { status: 403 });
}
// リクエストが期限切れか確認
const currentTimestamp = Math.floor(Date.now() / 1000);
if (currentTimestamp > timestamp + expiration) {
return new Response("署名付きURLは期限切れです", { status: 403 });
}
// URLを検証するために検証パラメータを削除
params.delete("verify");
url.search = params.toString();
// 検証のために認証するデータを構築
const dataToVerify = `${url.pathname}${timestamp}`;
// HMAC-SHA256で署名を検証
const isValid = await crypto.subtle.verify(
"HMAC",
key,
new Uint8Array([...atob(receivedMac)].map((char) => char.charCodeAt(0))),
encoder.encode(dataToVerify),
);
if (!isValid) {
return new Response("無効な署名", { status: 403 });
}
// 署名が有効な場合、リクエストの処理を続行
return fetch(request);
},
};