Files
frontend_v2/src/lib/crypto.ts
toom1996 d6cb616337 update
2026-09-13 21:39:50 +08:00

37 lines
1.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/lib/crypto.ts — 前端 JS 请求签名(反爬一层,非加密)
//
// 仅做 HMAC-SHA256 计算,由 api.ts 的公开请求原语附带 X-Sign / X-Sign-Ts / X-Sign-Nonce 头,
// 供后端 middleware.ClientSign 校验「请求大概率来自真前端」。
//
// 安全说明:secret 必须出现在前端 bundle 才能签名(对浏览器可见),故只是「提高成本」,
// 不能作为唯一防线;真正有效需配合后端按 IP 限流。生产请改用随机长串并同步两端。
const CLIENT_SIGN_SECRET = "dev-client-sign-secret-change-me"
const CLIENT_SIGN_TTL = 30 // 秒,签名有效期(与后端 client_sign.ttl_seconds 一致)
// 计算 HMAC-SHA256 签名。
// canonical = METHOD + "\n" + Path + "\n" + RawQuery + "\n" + ts + "\n" + nonce
// 与后端 middleware.ClientSign 的拼接方式严格一致(换行符分隔,顺序固定)。
export async function clientSign(
method: string,
fullPath: string,
rawQuery: string,
): Promise<{ sig: string; ts: string; nonce: string }> {
const ts = String(Math.floor(Date.now() / 1000))
const nonce = Math.random().toString(36).slice(2) + Date.now().toString(36)
const msg = [method, fullPath, rawQuery, ts, nonce].join("\n")
const enc = new TextEncoder()
const key = await crypto.subtle.importKey(
"raw",
enc.encode(CLIENT_SIGN_SECRET),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
)
const buf = await crypto.subtle.sign("HMAC", key, enc.encode(msg))
const sig = Array.from(new Uint8Array(buf))
.map((b) => b.toString(16).padStart(2, "0"))
.join("")
return { sig, ts, nonce }
}