Session绑定踩坑实录与解决方案分享

诸葛巧玲 安全 阅读 1,424
赞 14 收藏
二维码
手机扫码查看
反馈

我的写法,亲测靠谱

先说结论吧,Session绑定的核心就是保证用户请求和服务器会话的一致性。我一般这样处理:

Session绑定踩坑实录与解决方案分享

const session = require('express-session');
const FileStore = require('session-file-store')(session);

app.use(session({
  store: new FileStore(),
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true },
  name: '__session'
}));

这里有几个关键点要说下。首先是secret,这个绝对不能用简单的字符串,建议用环境变量或者随机生成的长字符串。我之前在某个项目里直接写了’123456’,结果被安全扫描工具直接报了高危漏洞。

关于resavesaveUninitialized这两个选项,很多人喜欢都设成true,其实没必要。resave设为false可以避免每次请求都重写session,减少不必要的IO操作。saveUninitialized保持true是因为有时候我们需要未初始化的session来做一些权限判断。

还有一个容易忽略的是cookie.secure,我习惯根据环境变量来设置,本地开发用false,生产环境强制https。记得有次上线忘记改这个配置,导致生产环境session一直存不进去,折腾了大半天。

这几种错误写法,别再踩坑了

说到踩坑,session这块真是能写出花来。最常见的错误写法就是直接把用户敏感信息存在session里:

// 错误示范
req.session.user = {
  id: user.id,
  password: user.password, // 绝对不要这么做!
  token: user.token       // 这也是个坑
};

这种写法太危险了,一旦session被劫持,用户的密码和token就全暴露了。正确的做法是只存用户id就够了:

// 正确写法
req.session.userId = user.id;

另一个常见错误是滥用内存存储:

// 不推荐的写法
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true
}));

这种写法在开发环境还行,但是一旦到生产环境,重启服务器所有session就都没了。而且多进程部署的时候,不同进程之间的session也无法共享。所以还是老老实实用专门的存储方案,比如redis、mongodb之类的。

实际项目中的坑

在实际项目中,session绑定最容易出问题的就是跨域和负载均衡场景。说几个我遇到过的典型案例:

  • 负载均衡下的session丢失:这种情况通常是因为多个服务器实例之间没有共享session存储。我之前在一个项目里用了nginx做负载均衡,结果发现用户一会儿登录一会儿登出,后来才发现是每个服务器实例都有自己独立的session存储。解决方法就是使用集中式的session存储,比如redis。
  • 移动端接口调用失败:这个问题特别隐蔽,主要是因为有些开发者在设置cookie的时候忘记考虑SameSite属性。我遇到过一个case,API接口明明正常返回,但是前端拿不到session,最后发现是因为chrome升级后默认设置了SameSite=Lax,而我们的API域名和前端域名不一样。
  • session过期时间设置不合理:有人喜欢把session有效期设得特别长,比如一周甚至一个月。这其实很危险,万一session被劫持,攻击者就能长期冒充用户。我个人的习惯是设置成1-2小时,然后配合refresh token机制来续期。

另外还要特别注意的是,不要在session里存太大或太多的数据。我就见过有人把整个购物车数据都塞进session的,结果导致每次请求都要传输大量无用数据,严重影响性能。

其他要注意的小细节

除了上面说的这些大问题,还有一些小细节也值得注意:

  1. session key命名:不要用默认的connect.sid,很容易被扫描工具识别。我一般会自定义成类似__sess这样的名字。
  2. 定期清理过期session:不管用什么存储方案,都要记得设置定期清理机制。不然时间一长,存储空间会被占满。
  3. 监控和报警:要对session相关的异常做好监控,比如session存储连接失败、读写超时等。我在项目里都会加上对应的报警机制。

以上是我总结的最佳实践,有更好的方案欢迎评论区交流。最近也在研究基于JWT的无状态认证方案,不过这是另一个话题了,有机会再分享。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论