和绝大多数框架一样,Rails对Session做了封装。封装之后的Session,我们通过session方法来访问。

比如,在存储的时候,我们可以直接通过:

 `session[:index] = ’some thing’

访问,读取类似。当要销毁这个session的时候,直接把nil赋给对应的变量接口,比如:

 `session[:index] = nil

这里要说的,不是Session的原理,或者说Rails对Session的封装结构。而是讲Rails里,Session的存储机制。

按照Rails Guide文档里的描述,Rails支持多种Session存储方式。官方实现里包括CookieStore、CacheStore、MemCacheStore三种存储方式。分别将Session数据存储在Cookie、Cache(默认是内存中)、MemCache中。默认采用的是第一种方式——Cookie。

这里Rails的默认规则——CookieStore就很坑爹了。这种方式下,Rails(在服务端)本身并存储任何的Sesison数据,而是把每个联接的Session数据加密后(SHA1方式加密,密钥位于 config/initializers/secret_token.rb 中),直接传递给客户端的Cookie存储。客户端每次将Cookie发给服务端后,Rails解密使用。

这样就带来非常严重的两个问题:

  • Session存储在客户端Cookie中——这尼玛跟Cookie有啥区别呢?
  • Session数据无法真正销毁。

第一个问题很容易理解。虽然说Rails有作加密处理,但是毕竟数据存储在客户端,在安全性方面显得非常的不靠谱。而且由于Cookie的存储大小限制,限制了Session的使用。

第二个问题就麻烦了,通常我们认为按照正常的Session处理机制,如果手动销毁(比如用户注销)了Sesion,那即便是重新伪造该session,获取到的,也只能相当于是一个全新的session。但Rails这种机制就不行。如果有人在用户注销成功前,手动截获了session数据(这尼玛简直太容易了)。然后即便重新注销,用户也可以通过篡改Header的方式,恢复这个会话。

如果你仍然坚持使用CookieStore作为存储媒介,那么,也有两种保护方式。

  • 在Rails 3中默认采用的,在 config/initializers/secret_token.rb 中配置secret_token选项。
  • 在Rails 4中默认采用的,在 config/initializers/secret_token.rb 中配置的secret_key_base选项。

第一个选项是在Rails 3中默认配置的选项,该选项的作用是,在发给客户端的Cookie中,除了保存有Session的数据,还保存了Session的签名。这种情况下,虽然客户端可以通过base64解密,获取并读到Session的数据,但是因为无法伪造这个签名,所以仍然不能篡改。

第二个选项是在Rails 4中默认的选项,该选项的作用是加密客户端Cookie的数据,这样客户端只能看到加密后的Session数据,而不能看到真实的数据。

最安全的做法,就是两项并用。这样一方面客户端无法伪造数据(无法通过签名验证),另一方面压根也就看不到真实的Session数据。