更新时间:2024-09-03 17:00:38
本示例展示如何利用 Edge Cloud Apps 函数和Web Crypto API对特定网站资源进行安全访问控制,以宠物商店的目标URL: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(`URL 已过期:${new Date(expiry)}`);
}
// 验证通过,允许访问资源
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()">进行签名</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 函数代替客户端对请求进行签名,也可以将其用于防盗链校验。