世界上最伟大的投资就是投资自己的教育

全场限时 5 折

首页Ruby
随风 · 练气

session 原理与实现 (rails 篇)

随风发布于3445 次阅读

1. 介绍

上一篇文章cookie 原理与实现 (rails 篇)有说过在 rails 是如何存取 cookie 的,而这一篇文章来讲讲 rails 是如何操作 session 的。

默认情况下,session 是保存在 cookie 中的。这又是怎么回事呢。众所周知,cookie 是存在客户端浏览器中的,把 session 放在 cookie 中,可以吗?答案当然是可以。那安全吗?还算安全。就算有人得到了那 cookie,它也解密不了,因为存在 cookie 中的 session 是通过对称加密处理的,没有密钥是得不到原串的。

rails 中关于存储 session 的配置是这样的:

# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store, key: '_rails365_session'

所以的 session 信息,都会存放到 cookies 中,且 key 为_rails365_session

2. ActionDispatch::Session::CookieStore

实现把 session 存放到 cookie 中的功能都依靠这个中间件:ActionDispatch::Session::CookieStore

来看下实现这个功能相关的源码:

# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb

# 得到非加密的session数据,就是原来的session数据
def unpacked_cookie_data(req)
  req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
    v = stale_session_check! do
      if data = get_cookie(req)
        data.stringify_keys!
      end
      data || {}
    end
    req.set_header k, v
  end
end

# session数据包含session_id,值为随机码
def write_session(req, sid, session_data, options)
  session_data["session_id"] = sid
  session_data
end

# 把session数据存储到cookie中
def set_cookie(request, session_id, cookie)
  cookie_jar(request)[@key] = cookie
end

def get_cookie(req)
  cookie_jar(req)[@key]
end

来看看unpacked_cookie_data的实现,它是得到解密后 session 的数据。

比如下面这样:

env["action_dispatch.request.unsigned_session_cookie"]
{
     "session_id" => "760be4b1069ab0c80ccade6d36f00355",
    "_csrf_token" => "QdtMjYciHnF8XqSCe0xr8nHo3N5pQdhNeKWhe5ZxOC4=",
          "admin" => true
}

或者

session
{
     "session_id" => "760be4b1069ab0c80ccade6d36f00355",
    "_csrf_token" => "QdtMjYciHnF8XqSCe0xr8nHo3N5pQdhNeKWhe5ZxOC4=",
          "admin" => true
}

上面的数据都可以在 controller 里得到。

3. rack/session/cookie.rb

也就是说,从客户端传给服务器的加密过的 cookie 已经被先被解密了。

是的,已经先被处理了。

cookie_store.rb的源码,有一行是这样的:

require 'rack/session/cookie'

是 rack 程序,我们找到其源码

# https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb

def unpacked_cookie_data(request)
  request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
    session_data = request.cookies[@key]

    if @secrets.size > 0 && session_data
      digest, session_data = session_data.reverse.split("--", 2)
      digest.reverse! if digest
      session_data.reverse! if session_data
      session_data = nil unless digest_match?(session_data, digest)
    end

    request.set_header(k, coder.decode(session_data) || {})
  end
end

def write_session(req, session_id, session, options)
  session = session.merge("session_id" => session_id)
  session_data = coder.encode(session)

  if @secrets.first
    session_data << "--#{generate_hmac(session_data, @secrets.first)}"
  end

  if session_data.size > (4096 - @key.size)
    req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
    nil
  else
    session_data
  end
end

def generate_hmac(data, secret)
  OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
end

可见,不仅解密,连加密也是在那个 rack 程序中实现的。

使用的加密算法是最常用的OpenSSL::HMAC算法。

这个算法使用的 secret 或 salt 是@secrets.first@secrets是在下面这里定义的:

# https://github.com/rails/rails/blob/d50d7094247aad5005cd1b47258ddf338b0dddd7/railties/lib/rails/application.rb#L383
def secrets
  @secrets ||= begin
    secrets = ActiveSupport::OrderedOptions.new
    yaml = config.paths["config/secrets"].first
    if File.exist?(yaml)
      require "erb"
      all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
      env_secrets = all_secrets[Rails.env]
      secrets.merge!(env_secrets.symbolize_keys) if env_secrets
    end

    # Fallback to config.secret_key_base if secrets.secret_key_base isn't set
    secrets.secret_key_base ||= config.secret_key_base
    # Fallback to config.secret_token if secrets.secret_token isn't set
    secrets.secret_token ||= config.secret_token

    secrets
  end
end

其实就是加载config/secrets.yml文件取得里面的secret_key_base参数的值,作为 secret。

也就是说,session 以 cookie 的方式存储的加密还是会依赖于secret_key_base参数的值,所以secret_key_base是不能泄漏的。

完结。

本站文章均为原创内容,如需转载请注明出处,谢谢。

0 条回复
暂无回复~~
喜欢
统计信息
    学员: 29188
    视频数量: 1985
    文章数量: 489

© 汕尾市求知科技有限公司 | Rails365 Gitlab | Qiuzhi99 Gitlab | 知乎 | b 站 | 搜索

粤公网安备 44152102000088号粤公网安备 44152102000088号 | 粤ICP备19038915号

Top