⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

6.2.2 Session 安全:存储与传输安全

本质还原

Session 是服务端保存用户状态的机制。与 JWT 不同,Session 数据存储在服务端,客户端只保存一个 Session ID。

Session 安全威胁

1. Session 劫持

攻击者通过各种手段获取用户的 Session ID,冒充用户身份。

防护措施

typescript
// 使用安全的 Cookie 设置
const sessionOptions = {
  cookie: {
    httpOnly: true,    // 防止 JS 读取
    secure: true,      // 仅 HTTPS 传输
    sameSite: 'strict', // 防止跨站请求携带
    maxAge: 24 * 60 * 60 * 1000 // 1 天
  }
}

2. Session 固定攻击

攻击者先获取一个有效的 Session ID,诱导用户使用该 ID 登录。

防护措施

typescript
// 登录成功后,重新生成 Session ID
async function login(user: User, session: Session) {
  // 销毁旧 session
  await session.destroy()
  
  // 创建新 session
  const newSession = await createSession()
  newSession.userId = user.id
  
  return newSession.id
}

3. Session 数据泄露

Session 存储不当导致数据泄露。

防护措施

typescript
// 不要在 session 中存储敏感信息
// ❌ 危险
session.password = user.password
session.creditCard = user.creditCard

// ✅ 安全:只存必要的标识符
session.userId = user.id
session.role = user.role

Session 存储方案

存储方式优点缺点适用场景
内存快速重启丢失、无法扩展开发环境
文件简单IO 慢、难扩展小型应用
数据库持久化查询开销中型应用
Redis快速、可扩展需额外维护生产环境

Redis Session 存储示例

typescript
import Redis from 'ioredis'

const redis = new Redis(process.env.REDIS_URL)

async function getSession(sessionId: string) {
  const data = await redis.get(`session:${sessionId}`)
  return data ? JSON.parse(data) : null
}

async function setSession(sessionId: string, data: object, ttl: number) {
  await redis.setex(
    `session:${sessionId}`,
    ttl,
    JSON.stringify(data)
  )
}

async function deleteSession(sessionId: string) {
  await redis.del(`session:${sessionId}`)
}

安全配置最佳实践

Session ID 生成

typescript
import { randomBytes } from 'crypto'

function generateSessionId(): string {
  // 使用加密安全的随机数
  return randomBytes(32).toString('hex')
}

Session 超时策略

typescript
const SESSION_CONFIG = {
  // 绝对超时:创建后 24 小时过期
  absoluteTimeout: 24 * 60 * 60 * 1000,
  
  // 空闲超时:30 分钟无活动过期
  idleTimeout: 30 * 60 * 1000,
}

async function validateSession(session: Session) {
  const now = Date.now()
  
  // 检查绝对超时
  if (now - session.createdAt > SESSION_CONFIG.absoluteTimeout) {
    throw new Error('Session 已过期')
  }
  
  // 检查空闲超时
  if (now - session.lastActivity > SESSION_CONFIG.idleTimeout) {
    throw new Error('Session 因不活动已过期')
  }
  
  // 更新最后活动时间
  session.lastActivity = now
}

敏感操作的会话验证

typescript
async function sensitiveOperation(session: Session) {
  // 敏感操作前检查 session 是否最近创建
  const sessionAge = Date.now() - session.createdAt
  const RECENT_THRESHOLD = 5 * 60 * 1000 // 5 分钟
  
  if (sessionAge > RECENT_THRESHOLD) {
    throw new Error('请重新登录以执行此操作')
  }
}

NextAuth 中的 Session 安全

typescript
// NextAuth 配置
export const authOptions: NextAuthOptions = {
  session: {
    strategy: 'database', // 使用数据库存储
    maxAge: 24 * 60 * 60,  // 24 小时
    updateAge: 60 * 60,    // 每小时更新
  },
  
  callbacks: {
    async session({ session, user }) {
      // 只暴露必要的用户信息
      session.user.id = user.id
      session.user.role = user.role
      // 不暴露敏感信息
      return session
    }
  }
}

Session 安全检查清单

  1. [ ] Session ID 使用加密安全的随机数生成
  2. [ ] Cookie 设置 HttpOnly、Secure、SameSite
  3. [ ] 登录成功后重新生成 Session ID
  4. [ ] 实现绝对超时和空闲超时
  5. [ ] Session 数据不包含敏感信息
  6. [ ] 生产环境使用 Redis 等可扩展存储