一、白名单与验证const allowSchemes = new Set(['myapp']) const allowDomains = new Set(['example.com','app.example.com']) function validScheme(s: string): boolean { return /^[a-z][a-z0-9+\-.]*$/.test(s) && allowSchemes.has(s) } function validDomain(d: string): boolean { return allowDomains.has(d.toLowerCase()) } 二、状态参数与签名import crypto from 'crypto' function signState(state: string, secret: Buffer): string { return crypto.createHmac('sha256', secret).update(state).digest('hex') } function verifyState(state: string, sig: string, secret: Buffer): boolean { return signState(state, secret) === sig } 三、回调URL过滤function sanitizeCallback(u: string): { ok: boolean; url?: string } { try { const x = new URL(u) if (!validDomain(x.hostname)) return { ok: false } if (!/^\/callback\/[a-z0-9_\-]+$/.test(x.pathname)) return { ok: false } const next = new URL(x.origin + x.pathname) return { ok: true, url: next.toString() } } catch { return { ok: false } } } 四、服务端校验示例type Req = { query: Record<string, string | undefined> } type Res = { status: (n: number) => Res; end: (b?: string) => void; redirect: (url: string) => void } function handleCallback(req: Req, res: Res, secret: Buffer) { const state = req.query['state'] || '' const sig = req.query['sig'] || '' const cb = req.query['cb'] || '' if (!verifyState(state, sig, secret)) return res.status(401).end('invalid_state') const s = sanitizeCallback(cb) if (!s.ok || !s.url) return res.status(400).end('invalid_callback') res.redirect(s.url) } 五、客户端触发示例(兼容)function openDeepLink(state: string, sig: string) { const scheme = 'myapp://open?state=' + encodeURIComponent(state) + '&sig=' + encodeURIComponent(sig) const universal = 'https://app.example.com/open?state=' + encodeURIComponent(state) + '&sig=' + encodeURIComponent(sig) const a = document.createElement('a') a.href = scheme document.body.appendChild(a) a.click() setTimeout(() => { window.location.href = universal }, 1500) } 六、验收清单Scheme与域名白名单生效;回调路径限制到`/callback/{id}`形式;拒绝非白名单或异常路径。`state`与签名校验通过;回调仅使用规范化URL,避免Open Redirect。客户端深链与通用链接兼容触发;失败时回退到通用链接。

发表评论 取消回复