一、风险背景与目标
- 风险:会话固定(Session Fixation)、CSRF跨站请求、XSS读取Cookie、子域泄露与范围过宽、持久会话未轮换。
- 目标:属性收敛最小化权限、会话登录与敏感操作后滚动ID、精确路径与域名作用域、SameSite控制与CSRF令牌协同。
二、策略基线与参数
- 必选属性:`Secure`、`HttpOnly`、`SameSite=Lax`;敏感场景可设`Strict`。
- 作用域:优先使用`__Host-`前缀,`Path=/`且不设置`Domain`。
- TTL与持久化:建议`Max-Age≤86400`,并在登录后、权限提升后执行会话ID滚动。
- 注销与回收:统一删除会话与刷新黑名单,立即失效。
三、服务端设置与统一构造
```ts
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滚动与绑定
```ts
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协同
```ts
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)
}
```
六、登录流程与注销处理示例
```ts
type Req = { headers: Record

发表评论 取消回复