一、风险背景与目标风险:会话固定(Session Fixation)、CSRF跨站请求、XSS读取Cookie、子域泄露与范围过宽、持久会话未轮换。目标:属性收敛最小化权限、会话登录与敏感操作后滚动ID、精确路径与域名作用域、SameSite控制与CSRF令牌协同。二、策略基线与参数必选属性:`Secure`、`HttpOnly`、`SameSite=Lax`;敏感场景可设`Strict`。作用域:优先使用`__Host-`前缀,`Path=/`且不设置`Domain`。TTL与持久化:建议`Max-Age≤86400`,并在登录后、权限提升后执行会话ID滚动。注销与回收:统一删除会话与刷新黑名单,立即失效。三、服务端设置与统一构造type CookieOpt = { secure: boolean; httpOnly: boolean; sameSite: 'Lax' | 'Strict' | 'None'; path: string; domain?: string; maxAge?: number } function buildCookie(name: string, value: string, opt: CookieOpt): string { const parts: string[] = [] parts.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`) parts.push(`Path=${opt.path}`) if (opt.domain) parts.push(`Domain=${opt.domain}`) if (opt.maxAge !== undefined) parts.push(`Max-Age=${opt.maxAge}`) parts.push(`SameSite=${opt.sameSite}`) if (opt.secure) parts.push('Secure') if (opt.httpOnly) parts.push('HttpOnly') return parts.join('; ') } type Res = { setHeader: (k: string, v: string | string[]) => void } function setSessionCookie(res: Res, id: string, ttl: number) { const name = '__Host-session' const header = buildCookie(name, id, { secure: true, httpOnly: true, sameSite: 'Lax', path: '/', maxAge: ttl }) res.setHeader('Set-Cookie', header) } function expireCookie(res: Res, name: string) { const header = buildCookie(name, '', { secure: true, httpOnly: true, sameSite: 'Lax', path: '/', maxAge: 0 }) res.setHeader('Set-Cookie', header) } 四、会话ID滚动与绑定function randomId(): string { return Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex') } type Session = { id: string; userId: string; uaHash: string; createdAt: number; ttl: number } function rotateSession(old: Session): Session { return { id: randomId(), userId: old.userId, uaHash: old.uaHash, createdAt: Date.now(), ttl: old.ttl } } function bindUa(ua: string): string { return require('crypto').createHash('sha256').update(ua).digest('hex') } function verifyUa(session: Session, ua: string): boolean { return session.uaHash === bindUa(ua) } 五、CSRF令牌与SameSite协同function issueCsrf(): string { return require('crypto').randomBytes(16).toString('hex') } function setCsrfCookie(res: Res, token: string) { const header = buildCookie('__Host-csrf', token, { secure: true, httpOnly: false, sameSite: 'Strict', path: '/', maxAge: 3600 }) res.setHeader('Set-Cookie', header) } function validateCsrf(headerToken: string | undefined, bodyToken: string | undefined): boolean { if (!headerToken || !bodyToken) return false return headerToken === bodyToken && /^[a-f0-9]{32}$/.test(headerToken) } 六、登录流程与注销处理示例type Req = { headers: Record<string, string | undefined>; body: any } function onLogin(req: Req, res: Res, userId: string) { const ua = req.headers['user-agent'] || '' const s: Session = { id: randomId(), userId, uaHash: bindUa(ua), createdAt: Date.now(), ttl: 86400 } setSessionCookie(res, s.id, s.ttl) const csrf = issueCsrf() setCsrfCookie(res, csrf) } function onSensitiveAction(req: Req, res: Res, current: Session) { const next = rotateSession(current) setSessionCookie(res, next.id, next.ttl) } function onLogout(res: Res) { expireCookie(res, '__Host-session') expireCookie(res, '__Host-csrf') } 七、校验清单与验收`Secure`与`HttpOnly`始终开启,`SameSite`默认`Lax`,敏感场景验证`Strict`。会话ID长度≥32字节并为不可预测随机数;登录与权限提升后执行滚动。作用域使用`__Host-`前缀,`Path=/`,禁止设置`Domain`以避免子域泄露。CSRF令牌长度与格式校验,头与体必须一致;跨站场景采用令牌而非放宽`SameSite`。TTL不超过`86400`并可按风险分级缩短;注销时立即回收并删除相关Cookie。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
1.874386s