背景与价值Magic Link降低密码负担,但若无签名与一次性治理易被滥用。统一签名、白名单与窗口策略可保障安全。统一规范一次性:链接含 `nonce`,验证后立即作废。过期窗口:`exp` 不超过15分钟,过期拒绝。来源白名单:链接跳转 `origin` 必须受控。核心实现签名生成与校验function enc(s: string): Uint8Array { return new TextEncoder().encode(s) } async function importHmac(secret: ArrayBuffer): Promise<CryptoKey> { return crypto.subtle.importKey('raw', secret, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']) } async function hmac(key: CryptoKey, data: string): Promise<string> { const raw = await crypto.subtle.sign('HMAC', key, enc(data)); const u = new Uint8Array(raw); let s=''; for (let i=0;i<u.length;i++) s+=String.fromCharCode(u[i]); return btoa(s).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'') } type Token = { email: string; nonce: string; exp: number; sig: string } const store = new Set<string>() const allowOrigins = new Set(['https://app.example.com']) function genId(): string { return Math.random().toString(36).slice(2) } function now(): number { return Date.now() } async function issueLink(email: string, key: CryptoKey, ttlMs = 900000): Promise<Token> { const t: Omit<Token,'sig'> = { email, nonce: genId(), exp: now() + ttlMs }; const payload = `${t.email}|${t.nonce}|${t.exp}`; const sig = await hmac(key, payload); store.add(t.nonce); return { ...t, sig } } function originAllowed(url: string): boolean { try { const u = new URL(url); return allowOrigins.has(u.origin) } catch { return false } } async function verifyLink(t: Token, key: CryptoKey, redirect: string): Promise<boolean> { if (!originAllowed(redirect)) return false; if (!store.has(t.nonce)) return false; if (now() > t.exp) return false; const payload = `${t.email}|${t.nonce}|${t.exp}`; const expect = await hmac(key, payload); const ok = expect === t.sig; if (ok) store.delete(t.nonce); return ok } 落地建议对链接实行一次性与过期窗口治理,并绑定跳转来源白名单。审计签发与验证事件,失败立即告警并阻断。验证清单`nonce` 是否一次性使用且过期后拒绝。跳转 `origin` 是否命中白名单,签名是否匹配。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
1.647674s