一、白名单与验证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。客户端深链与通用链接兼容触发;失败时回退到通用链接。

发表评论 取消回复