部署 多台机器上 Unicorn 共享 Session

6233843 · 2014年05月25日 · 最后由 jimrokliu 回复于 2014年08月11日 · 10525 次阅读
本帖已被设为精华帖!

Hi,各位 问一个找了几天都没有满意答案的问题,欢迎大家讨论。

Rails 默认的 Session 是 CookieStore,也就是存在 Cookie 里面,随着每次发送的 request 到服务器端,供服务器解析。

一旦我有多台机器,就会面临在 A 机器上登陆的用户可能下次请求被 redirect 到 B 机器而导致 Session 丢失。解决方案是使用数据库或者 Memcached 单独把 Session 存起来,这样多台机器都共同访问一个 Session 池,就不会发生上述问题了。

但我想,既然 CookieStore 是在 Cookie 里的,每次 request 里面既有 Cookie 也有 CookieSession,是不是 CookieStore 自然就能多机器共享 Session,而不用再搭建 Memcached 或者 DB 的 Session 池?

再请教大家都是怎么做的?有什么优缺点? 欢迎指教 :)

cookiestore 没有这个问题。

cookiestore 的限制是大小限制,cookies 不能超过大约 4K 大小,所以不要往 session 里面塞大的对象。

ip-hash ,简单粗暴

The method ensures that requests from the same client will always be passed to the same server except when this server is unavailable.

http://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash

cookiestore 适合不存储复杂对象的场景。如果你的应用不需要购物车这种复杂对象,你可以考虑 cookiestore,后台任何一个服务器退出都不会影响用户的请求。

#2 楼 @Rei 一般也不会往 session 里面塞其他东西吧

#3 楼 @libuchao 这个太过于简单粗暴,这样一搞,负载均衡完全没用呀

#4 楼 @jimrokliu 谢谢,我的猜测得到确认

#5 楼 @6233843 新手常见的是:

session[:user] = current_user

推荐的是

session[:user_id] = current_user.id

cookie 都是放在客户端,如果后台设计的差,直接篡改这个 cookie 就可以产生中间人攻击啦。还是用数据库来存吧。Mysql memory engine.

#9 楼 @xds2000 服务端 session 也依赖 cookie, 劫持保存在客户端的 session id 照样能产生中间人攻击, 没区别, cookiestore 保存好 secret key 安全性不会比传统 session 差。 mysql 内存表坑很多, 比如不支持大字段, 就算是 varchar 照样按定长算,没特殊要求尽量别用这东西。

还是先把 https 搞起来吧

@tnt 有几点不同意你的观点:

  1. “cookiestore 保存好 secret key 安全性不会比传统 session 差。” 首先,服务端 session 也是用 secret key 的。第二,session 存客户端,劫持后可以直接用这个账号登录。但如果 session 在服务端,这个合法用户在知道劫持后是可以通过退出账号,直接让劫持的 session 无效的。你说说这安全性差在什么地方。

就像 @Rei 说的 cookiestore 不会出现你所说的 session 丢失,只是大小有限制,如果你遇到了使用 cookiestore 但是确实类似的情况,你可以检查下你不同服务器的 secret key 是不是一样,sercet key 用来验证和签名 cookie 的,如果不一样,最后表现的情况就像是 session 丢失。

cookie_store,或 dalli_store 轻松解决

config/initializers/session_store.rb

Rails.application.config.session_store ActionDispatch::Session::CacheStore, :expire_after => 20.minutes

Gemfile

gem 'dalli'

#12 楼 @xds2000

第一点不是很一致么 (1) 无论是 cookie store 还是传统的 cookie(session id)<->server session, 就算再通过 secret key 加密, 劫持到 cookie 时已经算完了, 这方面除了 ssl 是没办法保障的。 (2) 不考虑劫持现象, 传统的 cookie(session id)<->server session 无法伪造, 但是 cookie store 的 session 在服务端 secret key 不泄漏的情况下, 同样无法伪造。 所以安全系数上是完全一致的。

第二点针对登出失效, 这是设计问题, 不是安全问题 cookie store 的 session 检测用户时同样需要存在一个 sign 做验证 (比如根据用户的密码和一个 token 字段), 用户登出完全可以重设生成这个 sign 所需要的 token, 也就是说当用户登出或者修改密码时,原先保存在 session 里的 sign 已经失效了, 当然也就未登录了。

@tnt 首先,我要明确争论的论点是:Session 存在客户端还是服务端, 我的观点是,存在客户端的 session 是有安全风险的,是对用户数据的不负责任的设计。

第二,对你 #15 楼#10 楼说的观点不赞同。 “cookie store 的 session 检测用户时同样需要存在一个 sign 做验证 (比如根据用户的密码和一个 token 字段)” 我为啥需要检测呢, 我本地已经有这个 session 了,我可以直接用就可以(假设 session 还没有过期)。

#12 楼 @xds2000

session 存客户端,劫持后可以直接用这个账号登录。但如果 session 在服务端,这个合法用户在知道劫持后是可以通过退出账号,直接让劫持的 session 无效的

劫持一样有效的,普通用户是很难知道自己是否被劫持的。

@huacnlee 是的,你说的是真实的情况。

@tnt 抱歉,我没有那事实和代码说话。但我看了你提到的淘宝 session 框架的技术。我觉的它的方案并不一定是普适的,任何技术都是有场景的。我们讨论的就是一般场景下的方案。而你提到的很多框架使用默认 cookie 来存 session 算是一个论据,我认可你这个理由。

我继续我的观点 存在客户端的 session 是有安全风险的,是对用户数据的不负责任的设计。,另外,我这里就是对通常情况的处理。任何观点在不同的场景下都是可以被打破的。

论点资料:

  1. Ruby on Rails Security Guide Django Security Guide 两个最流行的 WEB 框架对 session 存在 cookiestore 的说明,我认为如何用好 cookiestore 并不那么容易。

  2. Session storage and security in Rails 我看到的是有风险的存在。

  3. "Security Vulernability: Session Cookie Store" 用开源代码的 key 解 session.

一般情况下,#14 楼的方案就可以算是完美的,不赞成使用 cookie_store,有安全限制,存储限制,设计限制。

用 memcache 或 mysql memory engine 存 session 就不怕重启的时候用户的 session 掉一地么?这就是对用户数据负责?

重放攻击和 scret key 泄露问题在正确使用的前提下是可以避免的,谁能保证自己的服务器不重启?

用了好几年多台 unicorn 负载均衡的人表示从来没遇到过什么跨节点 session 丢失问题。cookiestore 本身就是无状态的,不会绑定到任何一台 web 节点。

说客户端存储 session 有风险的,请仔细阅读相关源码,然后根据源码的实现原理制定出一个可操作的攻击方案,贴到这里一起讨论。都是搞技术的,希望不要光打嘴炮。

从共享的问题讨论到了安全的问题。 很多我们所谓清楚的技术都是"拿来主义",很多是没有亲自实验过的。 放进 couchbase 节点自动同步如何?

咱们的回复 有点偏离 lz 的本意了, 不过既然说到这了, 咱们就继续说说 CookieStore 的一个弊端:

如果使用 CookieStore, 那么用户对应的 session cookie 就存到客户端, 这个时候 也就与服务器无关了, 也就是说你即使 logout 了,通过 copy 这个 cookie 还是可以访问 server 的。这个文章有个演示

那么怎么让客户端 cookie 失效呢

解决方法:

1、用户 主动 修改密码, 让之前的 cookie 失效.(但是 试想用户主动 修改密码的 频率 实在太低) 2、修改 secret_key, 是所有的 cookie 都失效 (代价太大了, 应该直接不用这个方案)

回到 lz 的 共享的 session 的问题:#14 楼 huacnlee 给出了一个大众的方案.

#23 楼 @meeasyhappy 用 Rails 默认的 CookieStore,靠谱的,保证 secret_key 安全就好了。

另外,纠正一下前面 #18 楼 @tnt 说淘宝是 Cookie 存储 Session 的事情,我们之前内部调用过淘宝主站的 Session,原理是 Cookie 里面有个 session key,真正的 Session 数据是存储在服务端的(具体什么里面我不清楚,猜测应该是 Memcached 之类的),取信息通过 session key 调用某些接口获取

感谢大家的踊跃发言,这篇帖子已经成为精华帖了。。。

#23 楼 @meeasyhappy cookie 的加密不光有 secret_key,还有个 token,每个用户的都不一样,退出后重置这个 token,cookie 就失效了,不会影响到其他用户

cookieSession 可以做到客户端共享,缺点是不安全。 如果是服务器端 session,一般是两个思路: 1.多个节点本地存,然后数据同步,我不知道 ruby 有没有现成的方案做数据同步,优点是数据读取快,缺点是同步存在时间差,数据一致性差。 2.使用集中式的缓存服务存 session,比如 memcached 或者 redis 存储。优点是数据一致性好,缺点是存在远程调用。 至于使用 db 做 session 存储的,也是一种方案,不过撑不住大的访问量。

cookiestore have no problem

#28 楼 @hmilym cookiestore has on problem

session 存在 cookie 在学习项目中可以用用,但在真实应用中不推荐,缺点:安全和费流量;存数据库性能不行,建议存集中式缓存,比如 memcache,14 楼 @huacnlee 已经给出方案,一般解决到这也就满足系统需求了;但是如果有人说当某台 cache 服务器宕机了,不就存在 session 丢失吗?可以要看业务影响面;这个可以解决,但是要看你这系统有没有必要花精力去解决这个问题;解决 cache 单点问题,可以同一份数据存在多个 cache 服务器上,保证高可用;当然没有绝对的,如果整个 cache 集群所在的机房断电了呢?难道要做多机房热备?额,扯远了。觉得一般应用用集中式缓存存 session 就够了。

#18 楼 @tnt 淘宝分享用加密 cookie 作为 session 方案(请搜"淘宝 session 框架")?淘宝不是在 coookie 里加密存 session 吧?据我了解是在服务器端用 Tair(集中式高可用缓存)存储的。

默默爱上了 redis,所以这个也许楼主愿意试试看,比 db session store 要轻巧一点。

https://github.com/redis-store/redis-rails

# config/initializers/session_store.rb
MyApplication::Application.config.session_store :redis_store

rack 代码里 session 下面的 cookie 实现,其实就是在浏览器里存储了 session 的 key,seesion 的 value 还是在内存的。叫 cookie based session: https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb 代码里还有个 memcache 的 session 实现,就是把 session 的 value 存储在 memcache 里: https://github.com/rack/rack/blob/master/lib/rack/session/memcache.rb 要实现共享 session,要考虑几方面: 1.安全,因此首选存在 server 2.稳定,不能因 server 端 crash 而丢失 session,要有持久化支持。 3.自动清除,客户端的 cookie 有效期是可以设置的,为避免 server 端 session 存储规模太大,也要支持 expire 后自动删除。 综上,使用基于 redis 和 mongodb 的 session 存储,都挺好: https://github.com/roidrage/redis-session-store https://github.com/yeezon/mongo_session 我在项目里,一直用 mongo_session。 padrino 里,指定 session 存储:

set :sessions, :use => Rack::Session::MongoSession, :config => {:host=>''...}

前几天怎好也在处理这个问题,多项目共享 Session,我是通过 Redis 实现的,这是我整理的步骤,楼主可以参考一下:http://www.cnblogs.com/richard1234/p/3783689.html

@120791589 这样是要单点登陆吧,咋和楼主说的不是一个方向

session 数据本身肯定要存服务器的,至于是内存、memchaced、redis 还是数据库,就见仁见智了。存到客户端的就是 session key。

#4 楼 @jimrokliu 有复杂对象购物车也可以用 cookie 存储数据阿,购物车利的东西完全可以放在 server 端阿

#37 楼 @wudixiaotie 主要是 cookie 有 4k 的长度限制,加上 rails 已经把用户的信息加密后存储在 cookie 中,你利用的空间不大。

#38 楼 @jimrokliu 完全可以只存储个用户标示,然后把相关的信息存到 server 端的数据库或者缓存层中,当然不能全放到 cookie 中了,上面有人都说过这样类似的方法了。

#39 楼 @wudixiaotie 这种情况就不是利用 cookie 存储复杂对象了。

这个页面在 safari 7.0.3 页面搜索找不到 lee 这个词。好奇怪。

#40 楼 @jimrokliu 不明白为什么你要用 cookie 存储复杂对象,或者非要在客户端存储复杂对象,这样作能得到什么明显的有利的地方么????拿到是为了实现而实现????

#42 楼 @wudixiaotie 我没有说过要用 cookie 存储复杂对象啊,我第一句话是 “cookiestore 适合不存储复杂对象的场景。如果你的应用不需要购物车这种复杂对象,你可以考虑 cookiestore,后台任何一个服务器退出都不会影响用户的请求。”

#40 楼 @jimrokliu 不知道你这句话是什么意思?

#44 楼 @wudixiaotie 是你问 “不明白为什么你要用 cookie 存储复杂对象” 呀,我说没有这回事呀。

#45 楼 @jimrokliu "这种情况就不是利用 cookie 存储复杂对象了。"这是啥意思?????

#46 楼 @wudixiaotie 是这样的,当你 cookie 只是存储一个用户 id,或者购物车 id,真正的内容存储在 database 中,这样不是利用 cookie 存储复杂对象。我所指的用 cookie 存储复杂对象是利用 cookie 的 4k 大小,将一些信息序列话到 cookie 中。在 cookie 中存储有一些直接的好处,最直接的就是可以降低后端 failover 的复杂性,因为状态完全在 cookie 中,新增加服务器或者 offline 服务器都不会触发用户端的再登陆。当然,后端可以用 redis 的群集,数据库群集来实现可靠的 session 存储,但会增加系统整体的复杂性。

#47 楼 @jimrokliu 我感觉,与其花时间死扣 4k 的 cookie 的存储空间,不如把经历用在其他的地方,例如你上面自己提出的解决方案,首先你要明白一点,cookie 本来就不是用来存储复杂对象的。再说,即便你想到办法把购入车的内容存到 cookie 里面,难道你要每个用户的购物车限制大小么???购物车东西过多,cookie 溢出就告诉用户,hi 哥们,你少买点东西吧,或者你该结帐了。。。你这网站还想继续下去么????cookie 设计的目的是为了保存用户的登录状态,习惯设置等等的信息,在用户关闭浏览器再次打开后不需要重新设置。你买个自行车然后在飞机论坛发帖子询问怎么让自行车飞起来,这完全是强人所难么!!!!!!!!!!

#48 楼 @wudixiaotie 不是所有的系统都需要有个购物车的,楼主最早的问题是能不能省略掉后端的 database store。所以我说 “不存储复杂对象的话是可以的。”

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册