背景与价值WebSocket不受同源策略约束,需在握手层进行来源与令牌治理并限制速率,防止跨站滥用与资源耗尽。统一规范来源白名单:校验 `Origin` 并匹配受控域。令牌校验:握手时校验访问令牌或签名参数。速率限制与心跳:限制握手频次与断线重连节奏,心跳检测保活。核心实现握手校验与速率限制type Req = { headers: Record<string, string | undefined>; url: string } type Res = { status: (n: number) => Res; end: (b?: string) => void } const allowOrigins = new Set(['https://app.example.com','https://admin.example.com']) function originAllowed(o: string | undefined): boolean { if (!o) return false; try { const u = new URL(o); return allowOrigins.has(u.origin) } catch { return false } } function validToken(t: string | undefined): boolean { return !!t && /^[A-Za-z0-9_\-\.]{16,128}$/.test(t) } class HandshakeGate { last = new Map<string, number>(); now(): number { return Date.now() }; allow(key: string, windowMs: number): boolean { const n = this.now(); const p = this.last.get(key) || 0; if (n - p < windowMs) return false; this.last.set(key, n); return true } } const gate = new HandshakeGate() function wsAuth(req: Req, res: Res): boolean { const origin = req.headers['origin'] if (!originAllowed(origin)) { res.status(403).end('forbidden'); return false } const u = new URL(req.url, 'https://app.example.com') const token = u.searchParams.get('token') || req.headers['sec-websocket-protocol'] if (!validToken(token || undefined)) { res.status(401).end('unauthorized'); return false } const key = (origin || '') + '|' + (token || '') if (!gate.allow(key, 3000)) { res.status(429).end('rate_limited'); return false } return true } 心跳检测与断线重连节奏class Heartbeat { intervalMs: number; timeoutMs: number; last = 0; constructor(intervalMs = 30000, timeoutMs = 60000) { this.intervalMs = intervalMs; this.timeoutMs = timeoutMs } tick() { this.last = Date.now() } expired(): boolean { return Date.now() - this.last > this.timeoutMs } } 落地建议握手阶段同时校验 `Origin` 与令牌,并限制同源同令牌的握手频次。实施心跳与断线重连退避策略,避免快速重试导致资源耗尽。审计连接与关闭事件,记录来源与令牌摘要以便追踪。验证清单握手是否校验 `Origin` 与令牌,速率限制是否生效。心跳是否按间隔发送与过期断开。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
2.077654s