---
title: Magic Link无密码登录治理(签名/一次性/过期窗口)最佳实践
keywords:
- Magic Link
- HMAC
- 过期窗口
- 一次性
- nonce
- origin白名单
description: 通过HMAC签名的一次性Magic Link与过期窗口、origin白名单校验,安全落地无密码登录并阻断链接滥用与重放。
categories:
- 文章资讯
- 编程技术
---
背景与价值
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是否命中白名单,签名是否匹配。

发表评论 取消回复