阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > Cookie 操作与安全设置

Cookie 操作与安全设置

作者:陈川 阅读数:1573人阅读 分类: Node.js

Cookie 的基本概念

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于会话状态管理、个性化设置以及行为跟踪等场景。在 Koa2 中,Cookie 的操作通常通过 ctx.cookies 接口实现。

// 设置一个简单的 Cookie
ctx.cookies.set('username', 'JohnDoe', {
  httpOnly: true,
  maxAge: 24 * 60 * 60 * 1000 // 1天
});

Koa2 中的 Cookie 操作

Koa2 提供了 ctx.cookies 对象来方便地操作 Cookie。这个对象提供了 getset 方法,分别用于读取和设置 Cookie。

设置 Cookie

设置 Cookie 时可以指定多个选项:

ctx.cookies.set('token', 'abc123', {
  domain: '.example.com',    // 可访问的域名
  path: '/admin',            // 可访问的路径
  maxAge: 86400000,          // 有效期(毫秒)
  expires: new Date('2023-12-31'), // 过期时间
  httpOnly: true,            // 仅HTTP访问
  secure: true,              // 仅HTTPS传输
  sameSite: 'strict'         // 同站策略
});

读取 Cookie

读取 Cookie 非常简单:

const username = ctx.cookies.get('username');
console.log(username); // 输出: JohnDoe

删除 Cookie

删除 Cookie 实际上是通过设置一个过期时间为过去的时间来实现的:

ctx.cookies.set('username', null, {
  maxAge: -1
});

Cookie 的安全设置

Cookie 的安全设置对于防止跨站脚本攻击(XSS)和跨站请求伪造(CSRF)至关重要。

HttpOnly 标志

ctx.cookies.set('sessionId', 's3cr3t', {
  httpOnly: true // 防止JavaScript访问
});

Secure 标志

ctx.cookies.set('auth', 'token123', {
  secure: true // 仅在HTTPS连接中传输
});

SameSite 属性

SameSite 属性可以防止CSRF攻击:

ctx.cookies.set('csrftoken', 'randomvalue', {
  sameSite: 'strict' // 或 'lax'
});

Cookie 签名

Koa2 支持对 Cookie 进行签名,防止客户端篡改:

// 在应用初始化时设置签名密钥
app.keys = ['my secret key'];

// 设置签名Cookie
ctx.cookies.set('cart', JSON.stringify(items), {
  signed: true
});

// 读取签名Cookie
const cart = ctx.cookies.get('cart', { signed: true });

Cookie 与 Session

在 Koa2 中,Cookie 常用来实现 Session:

const sessionId = ctx.cookies.get('sessionId', { signed: true });
if (!sessionId) {
  const newSessionId = generateSessionId();
  ctx.cookies.set('sessionId', newSessionId, {
    signed: true,
    httpOnly: true
  });
  // 初始化session数据
}

常见问题与解决方案

Cookie 大小限制

单个 Cookie 通常不应超过 4KB。对于大量数据,可以考虑:

// 将数据存储在服务端,仅通过Cookie传递标识符
ctx.cookies.set('dataRef', 'unique-id', {
  httpOnly: true
});
// 在服务端存储实际数据
dataStore.set('unique-id', largeData);

多域名共享 Cookie

ctx.cookies.set('shared', 'value', {
  domain: '.example.com' // 所有子域名共享
});

浏览器兼容性

对于不支持 SameSite 的老旧浏览器,需要额外措施:

// 设置双重Cookie
ctx.cookies.set('legacy_session', 'value', {
  secure: true,
  httpOnly: true
});
ctx.cookies.set('__Secure-session', 'value', {
  secure: true,
  httpOnly: true,
  sameSite: 'none'
});

性能优化

减少 Cookie 数量

合并多个小Cookie:

// 不推荐
ctx.cookies.set('pref1', 'value1');
ctx.cookies.set('pref2', 'value2');

// 推荐
const preferences = {
  pref1: 'value1',
  pref2: 'value2'
};
ctx.cookies.set('preferences', JSON.stringify(preferences));

静态资源域名分离

为静态资源使用无Cookie域名:

<!-- 主站 -->
<script src="https://www.example.com/app.js"></script>

<!-- 静态资源使用独立域名 -->
<img src="https://static.example.com/image.jpg">

实际应用示例

购物车实现

// 添加商品到购物车
router.post('/cart/add', async (ctx) => {
  const { productId, quantity } = ctx.request.body;
  let cart = JSON.parse(ctx.cookies.get('cart') || {};
  
  cart[productId] = (cart[productId] || 0) + quantity;
  
  ctx.cookies.set('cart', JSON.stringify(cart), {
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30天
    httpOnly: true,
    signed: true
  });
  
  ctx.body = { success: true };
});

用户偏好设置

// 保存用户主题偏好
router.post('/preferences/theme', async (ctx) => {
  const { theme } = ctx.request.body;
  
  ctx.cookies.set('userPrefs', JSON.stringify({
    ...JSON.parse(ctx.cookies.get('userPrefs') || '{}'),
    theme
  }), {
    maxAge: 365 * 24 * 60 * 60 * 1000, // 1年
    httpOnly: true
  });
  
  ctx.body = { success: true };
});

安全最佳实践

  1. 始终为敏感Cookie设置HttpOnly和Secure标志
  2. 对重要数据使用签名Cookie
  3. 合理设置SameSite属性
  4. 避免在Cookie中存储敏感信息
  5. 定期轮换签名密钥
  6. 设置合理的过期时间
// 安全Cookie示例
ctx.cookies.set('authToken', generateToken(), {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 3600000, // 1小时
  signed: true
});

调试与测试

查看Cookie

在Chrome开发者工具的Application面板中可以查看和修改Cookie。

单元测试示例

const request = require('supertest');
const app = require('../app');

describe('Cookie测试', () => {
  it('应该设置Cookie', async () => {
    const res = await request(app)
      .get('/set-cookie')
      .expect(200);
    
    expect(res.headers['set-cookie']).toBeDefined();
  });
});

与其他技术的结合

与JWT结合

// 设置JWT作为HttpOnly Cookie
ctx.cookies.set('jwt', generateJWT(user), {
  httpOnly: true,
  secure: true,
  maxAge: 3600000
});

// 在中间件中验证
const jwtMiddleware = async (ctx, next) => {
  const token = ctx.cookies.get('jwt');
  if (!token) {
    ctx.throw(401, '未授权');
  }
  
  try {
    ctx.state.user = verifyJWT(token);
    await next();
  } catch (err) {
    ctx.throw(401, '无效令牌');
  }
};

与CSRF防护结合

// 生成CSRF令牌
const csrf = require('koa-csrf');
app.use(csrf());

// 在表单中包含CSRF令牌
router.get('/form', async (ctx) => {
  ctx.body = `
    <form action="/submit" method="POST">
      <input type="hidden" name="_csrf" value="${ctx.csrf}">
      <!-- 其他表单字段 -->
    </form>
  `;
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌