update
This commit is contained in:
36
src/lib/crypto.ts
Normal file
36
src/lib/crypto.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user