安全 详解 Cookie 和 Session 关系和区别

HongJack · June 26, 2017 · Last by baurine replied at February 03, 2018 · 16311 hits

作者 | 个推 web 前端架构师 姜季廷

在技术面试中,经常被问到“说说 Cookie 和 Session 的区别”,大家都知道,Session 是存储在服务器端的,Cookie 是存储在客户端的,然而如果让你更详细地说明,你能说出几点?今天个推君就和大家谈谈“Cookie 和 Session”的那些事儿。

Cookie 是什么?

从它的词语本身含义来看: Cookie: n. 饼干;小甜点 N-COUNT A cookie is a piece of computer software which enables a website you have visited to recognize you if you visit it again. 再次访问某一网站时,能令网站识别访问人的计算机软件。

Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息。如何识别特定的客户呢?cookie 就可以做到。每次 HTTP 请求时,客户端都会发送相应的 Cookie 信息到服务端。它的过期时间可以任意设置,如果你不主动清除它,在很长一段时间里面都可以保留着,即便这之间你把电脑关机了。

既然它是存储在客户端的,换句话说通过某些手法我就可以篡改本地存储的信息来欺骗服务端的某些策略,那该怎么办呢?我们先按下不表,来看看另外一位朋友 —— Session。

Session 是什么?

同样,我们先来看看释义: Session: 普通释义:n. 会议;(法庭的)开庭;(议会等的)开会;学期;讲习会 计算机释义:会话

Session 是在无状态的 HTTP 协议下,服务端记录用户状态时用于标识具体用户的机制。它是在服务端保存的用来跟踪用户的状态的数据结构,可以保存在文件、数据库或者集群中。在浏览器关闭后这次的 Session 就消失了,下次打开就不再拥有这个 Session。其实并不是 Session 消失了,而是 Session ID 变了,服务器端可能还是存着你上次的 Session ID 及其 Session 信息,只是他们是无主状态,也许一段时间后会被删除。

实际上 Cookie 与 Session 都是会话的一种方式。它们的典型使用场景比如“购物车”,当你点击下单按钮时,服务端并不清楚具体用户的具体操作,为了标识并跟踪该用户,了解购物车中有几样物品,服务端通过为该用户创建 Cookie/Session 来获取这些信息。

如果你的站点是多节点部署,使用 Nginx 做负载均衡,那么有可能会出现 Session 丢失的情况(比如,忽然就处于未登录状态)。这时可以使用 IP 负载均衡(IP 绑定 ip_hash,每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 Session 的问题),或者将 Session 信息存储在集群中。在大型的网站中,一般会有专门的 Session 服务器集群,用来保存用户会话,这时可以使用缓存服务比如 Memcached 或者 Redis 之类的来存放 Session。

目前大多数的应用都是用 Cookie 实现 Session 跟踪的。第一次创建 Session 时,服务端会通过在 HTTP 协议中反馈到客户端,需要在 Cookie 中记录一个 Session ID,以便今后每次请求时都可分辨你是谁。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?建议使用 URL 重写技术进行会话跟踪,即每次 HTTP 交互,URL 后面都被附加上诸如 sid=xxxxx 的参数,以便服务端依此识别用户。

换个姿势~

客户端和服务端之间的通信交流,可以这样简单理解: 比如当你在个推技术分享沙龙上觉得某位讲师讲得很好,在会后问了他几个问题,他对你这些问题进行了回答,这就是一个会话。但这个讲师太受欢迎,于是工作人员收集问题,并给每个提问者一个号码牌,讲师按照号码牌依次给出相应解答并告诉相应的人。这就是 Session。一段时间后,当你再次遇见这位讲师,他发现你身上有上次回复你的答案,知晓你是那个好学的程序猿。于是你欣喜若狂,哇塞,讲师居然认出我了,这就是 Cookie,你的小甜点。客户端好比听课的技术爱好者,服务端就是这位讲师。

Cookie 还可以在一些方便用户的场景下使用。比如你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以被写到 Cookie 里面,当访问网站时,网站页面的脚本可以读取这个信息,自动填写用户名,方便用户使用,给用户一点甜头。

总结语:

1、Cookie 在客户端(浏览器),Session 在服务器端。 2、Cookie 的安全性一般,他人可通过分析存放在本地的 Cookie 并进行 Cookie 欺骗。在安全性第一的前提下,选择 Session 更优。重要交互信息比如权限等就要放在 Session 中,一般的信息记录放 Cookie 就好了。 3、单个 Cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 Cookie。 4、Session 可以放在 文件、数据库或内存中,比如在使用 Node 时将 Session 保存在 redis 中。由于一定时间内它是保存在服务器上的,当访问增多时,会较大地占用服务器的性能。考虑到减轻服务器性能方面,应当适时使用 Cookie。 5、Session 的运行依赖 Session ID,而 Session ID 是存在 Cookie 中的,也就是说,如果浏览器禁用了 Cookie,Session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 Session ID)。 6、用户验证这种场合一般会用 Session。因此,维持一个会话的核心就是客户端的唯一标识,即 Session ID。

题外话,那么话说 Session Cookie 能被篡改么? 理论上可以,只要改变了连接时的 Session ID 就可以了~

rails 的 session 依赖于 cookie 么?我理解好像是依赖的。

我有几个纯前端应用,如何做能让用户在这几个应用间无需多次登陆?

“大家都知道,Session 是存储在服务器端的,Cookie 是存储在客户端的”

面试 Rails 职位这么回答的话,可以回家等通知了。

Reply to Rei

哈哈哈哈哈哈哈哈哈哈哈哈

Reply to chenge

默认基于 cookie,你可以配置的,甚至存在数据库里

Reply to ecnelises

我理解是需要配合吧。如果没有 cookie,服务器怎么知道是哪个 session 呢?需要 session id 吧。

Reply to chenge

CookieBased session 的原理是把 session 转码之后直接存 cookie 里,再加上一个签名用于验证,类似 jwt

jwt 不适用于存放敏感信息,session 则没有这样的问题,并不认同将 session 转码存放 cookie,这样的理解

Reply to Rei

哈哈哈~ 毕竟头图上就放了两个 php 嘛,肯定是说的 php 里的情况咯

Reply to wayne712

你的不认同可能源自你已经将 session 与 敏感信息 划上了等号,但是我觉得这样的理解是有待商榷的 如果你在知道 CookieBased session 并不能阻止其他人看到其中的信息的情况下,不在里面放入敏感信息不就好了

挺多框架默认方案就是 CookieBased session,为什么呢?因为成本低呀。 而且也没有哪一家在文档里面说了 session 可以安心的放入敏感信息的吧

Reply to IChou

Rails 现在的 cookie store 是加密的,可以放敏感信息,主要限制还是体积。

Reply to Rei

咦 我记得是没有的呢,难道什么时候改了?

Reply to jetspeed

单点登入

Reply to IChou

If you only have secret_token set, your cookies will be signed, but not encrypted. This means a user cannot alter their user_id without knowing your app's secret key, but can easily read their user_id. This was the default for Rails 3 apps.
If you have secret_key_base set, your cookies will be encrypted. This goes a step further than signed cookies in that encrypted cookies cannot be altered or read by users. This is the default starting in Rails 4.

http://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html

本文的各种疏漏,以及排版,都无力吐槽

昂~ 查了下 果然是我理解错了 之前有个 php 的项目需要取到 Rails 的 cookie 里的内容,就去实现了一套 php 下面的 ruby_ marshal 序列化和反序列化。从那以后我就一直以为 Rails 的 session 只有 signature 没有真正的加密,原来只是我们那个项目特意关闭了加密而已 😂

PS: 顺便贴下 php 实现 ruby_ marshal 的地址,万一有人感兴趣呢,哈哈~ https://github.com/sudiyi/ruby_marshal

Reply to marksloan

哪些疏漏,可以修正出来让大家学习一下。

Reply to marksloan

😁 欢迎提修改意见哦~

Reply to chenge

这不是在面试的时候说的哦~

Reply to jetspeed

由于网关处理鉴权,将登录的用户信息以参数形式往下传~同域名的话,只要做一层统一网关就好啦

参数写在 url 里?别人可以看到了?

Reply to Rei

666!!!!

Reply to IChou

Rails 的 Session 还可以以 JSON 形式存在的 http://blog.bigbinary.com/2014/12/23/migrating-existing-session-cookies-while-upgrading-to-rails-4-1-and-above.html Rails 4.1 后这样是默认了

“大家都知道,Session 是存储在服务器端的,Cookie 是存储在客户端的”,我学了假的 session 假的 cookie

Reply to Rei

session 在服务器端,cookie 在客户端,这个理解有问题吗?

Reply to fengkuok

HTTP 是无状态的协议,请求之间记录用户状态需要用到 Session 机制,但存储 Session 的方式有多种,可以存储在服务器,例如数据库、文件,也可以存储在 cookie 中(rails 默认方式)

Reply to Rei

CookieStore,明白了,多谢!

以下是我在看《Agile Web Development Rails 5》时所做的笔记,不知道理解得是不是完全正确。

Rails Sessions

session 用来保持状态,不要和 cookie 混淆,它们只是有一点点交集而已。

Session Storage

Session 的存储方式 session_store,在 rails 中多达 6 种:

  • :cookie_store
  • :active_record_store
  • :drb_store
  • :mem_cache_store
  • :memory_store
  • :file_store

这 6 种方式,除了第一种 :cookie_store 是存储在客户端,其余都是在服务端进行状态的持久化。而第一种也是最常用的方式,这就是为什么我们很容易把它和 cookie 混淆。cookie 是实现 session 的一种手段。

我认为,人们常说的 cookie 有广义和狭义之分,广义的 cookie,就是被浏览器客户端持久化的这些对象,会在每次请求时被浏览器自动携带,它包括狭义的 cookie 和 session。狭义的 cookie 就是所有的 cookie 中除去 session 的那部分。

如果采用 :cookie_store 方式的 session,数据失效很好处理,关闭浏览器,再打开浏览器,cookie 中的 session 部分就会被自动清空,所以这种方式 session 的有效周期维持在一个浏览器进程时长。这种方式将减轻服务端逻辑。

如果采用服务端方式的 session,session 的生命周期是永久的,因此必须借助过期时间来清除过期的 session。另外,要注意,采用服务端方式的 session,并不是说就不需要往客户端存储任何数据了,还是需要的,只是只需要存储一个 session_id,然后通过 session_id 到服务端再去拿对应的其它数据。

You need to Sign in before reply, if you don't have an account, please Sign up first.