目录
Cookie
什么是Cookie
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
Cookie分类
会话 Cookie:会话 Cookie 是临时 Cookie,当前会话结束(浏览器退出)时 Cookie 会被删除。会话 Cookie 和持久 Cookie 的区别在于过期时间,如果设置了 Discard 参数(Cookie 版本1)或者没有设置 Expires(Cookie 版本 0)或 Max-Age(Cookie 版本1)设置过期时间,则此 Cookie 为会话 Cookie。
持久 Cookie:持久 Cookie 会存储在用户的硬盘上,浏览器(客户端)退出,然后重新启动后 Cookie 仍然存在。
Cookie版本
Cookie 有两个版本,一个是版本 0(Netscape Cookies)和版本 1(RFC 2965),目前大多数服务器使用的 Cookie 0。
Cookie工作原理
Cookie 就像服务器给用户贴的标签,用户访问一个 web 站点的时候,服务器就可以通过这个标签来识别是哪一个用户。Cookie 中包含了一个有名字=值(name=value)这样的信息构成的任意列表,并通过 Set-Cookie 或 Set-Cookie2 Http 响应(扩展)的 header 来设置标签。
cooke 可以包含任意信息,但他们通常都只包含一个服务器为了进行追踪而产生的独特的识别码。浏览器会记住从服务器返回的 Set-Cookie 或 Set-Cookie2 header 中的 Cookie 内容,并将 Cookie 集存储在浏览器的 Cookie 数据库中。将来用户访问统一站点时,浏览器会选中那个服务器贴到用户上的那些 Cookie,并请求的 header 中将起传给服务器。
Cookie详解
创建cookie
Web 服务器通过发送一个称为 Set-Cookie 的 HTTP 消息头来创建一个 cookie,Set-Cookie 消息头是一个字符串,其格式如下(中括号中的部分是可选的):
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
消息头的第一部分,value 部分,通常是一个 name=value 格式的字符串。事实上,这种格式是原始规范中指定的格式,但是浏览器并不会对 cookie 值按照此格式来验证。实际上,你可以指定一个不含等号的字符串,它同样会被存储。然而,最常用的使用方式是按照 name=value 格式来指定 cookie 的值(大多数接口只支持该格式)。
当存在一个 cookie,并允许设置可选项,该 cookie 的值会在随后的每次请求中被发送至服务器,cookie 的值被存储在名为 Cookie 的 HTTP 消息头中,并且只包含了 cookie 的值,忽略全部设置选项。例如:
Cookie: value
通过 Set-Cookie 指定的可选项只会在浏览器端使用,而不会被发送至服务器端。发送至服务器的 cookie 的值与通过 Set-Cookie 指定的值完全一样,不会有进一步的解析或转码操作。如果请求中包含多个 cookie,它们将会被分号和空格分开,例如:
Cookie: value1; value2; name1=value1
服务器端框架通常包含解析 cookie 的方法,可以通过编程的方式获取 cookie 的值。
cookie编码
对于 cookie 的值进行编码一直都存在一些困惑。普遍认为 cookie 的值必须经过 URL 编码,但其实这是一个谬论,尽管通常都这么做。原始规范中明确指出只有三个字符必须进行编码:分号、逗号和空格,规范中还提到可以进行 URL 编码,但并不是必须,在 RFC 中没有提及任何编码。然而,几乎所有的实现都对 cookie 的值进行了一系列的 URL 编码。对于 name=value 格式,通常会对 name 和 value 分别进行编码,而不对等号 = 进行编码操作。
cookie过期时间选项
紧跟 cookie 值后面的每个选项都以分号和空格分开,每个选择都指定了 cookie 在什么情况下应该被发送至服务器。第一个选项是过期时间(expires),指定了 cookie 何时不会再被发送至服务器,随后浏览器将删除该 cookie。该选项的值是一个 Wdy, DD-Mon-YYYY HH:MM:SS GMT 日期格式的值,例如:
Set-Cookie: name=Nicholas; expires=Sat, 02 May 2009 23:38:25 GMT
没有设置 expires 选项时,cookie 的生命周期仅限于当前会话中,关闭浏览器意味着这次会话的结束,所以会话 cookie 仅存在于浏览器打开状态之下。这就是为什么为什么当你登录一个 Web 应用时经常会看到一个复选框,询问你是否记住登录信息:如果你勾选了复选框,那么一个 expires 选项会被附加到登录 cookie 中。如果 expires 设置了一个过去的时间点,那么这个 cookie 会被立即删掉。
Cookie流程
大部分 web 网站都会使用到 cookie,使用它的基本流程可以概括为 3 步,浏览器向服务器请求一个从未请求过的 web 页面:
GET /index.html HTTP/1.1Host: www.haicoder.net
...
浏览器会在接下来的 http 请求的 header 中把以上 cookie 原样返回给服务器:
HTTP/1.0 200 OKContent-type: text/htmlSet-Cookie: theme=lightSet-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2020 10:18:14 GMT
...
服务器返回浏览器此页面,并在返回 http 包的 header 中设置 set-cookie 属性:
GET /spec.html HTTP/1.1Host: www.haicoder.netCookie: theme=light; sessionToken=abc123
…
上面示例设置了两个 cookies: 第一个是 theme,由于没有设置它的 Expires 或 Max-Age 属性,所以是个 session cookie,它会在浏览器关闭时直接被删除掉。 第二个是 SessionToken,它有过期时间,所以只会在指定的过期时间后才会被删除(也可手动删除)。
浏览器访问时,会把上一步的 cookies 设置在 header 中原样返回给服务器,当然不需要再指定 cookie 的其它属性,只需要把 key-value 返回即可。服务器根据设置的 cookies 就能知道标识这个客户,把无状态的 HTTP 请求变成了有状态的请求。
浏览器的 Cookie 头部信息如下:
Cookie使用
会话管理
- 记录用户的登录状态是 cookie 最常用的用途。通常 web 服务器会在用户登录成功后下发一个签名来标记 session 的有效性,这样免去了用户多次认证和登录网站。
- 记录用户的访问状态,例如导航啊,用户的注册流程啊。
个性化信息
- Cookie 也经常用来记忆用户相关的信息,以方便用户在使用和自己相关的站点服务。例如:ptlogin 会记忆上一次登录的用户的 QQ 号码,这样在下次登录的时候会默认填写好这个 QQ 号码。
- Cookie 也被用来记忆用户自定义的一些功能。用户在设置自定义特征的时候,仅仅是保存在用户的浏览器中,在下一次访问的时候服务器会根据用户本地的 cookie 来表现用户的设置。例如 google 将搜索设置(使用语言、每页的条数,以及打开搜索结果的方式等等)保存在一个 COOKIE 里。
记录用户的行为
最典型的是公司的 TCSS 系统。它使用 Cookie 来记录用户的点击流和某个产品或商业行为的操作率和流失率。当然功能可以通过 IP 或 http header 中的 referrer 实现,但是 Cookie 更精准一些。
Cookie属性
一般 Cookie 所具有的属性,包括:
domain选项
domain,指定了 cookie 将要被发送至哪个或哪些域中。默认情况下,domain 会被设置为创建该 cookie 的页面所在的域名,所以当给相同域名发送请求时该 cookie 会被发送至服务器。domain 选项可用来扩充 cookie 可发送域的数量,例如:
Set-Cookie: name=Nicholas; domain=haicoder.net
像 Yahoo! 这种大型网站,都会有许多 name.yahoo.com 形式的站点(例如:my.yahoo.com, finance.yahoo.com 等等)。将一个 cookie 的 domain 选项设置为 yahoo.com,就可以将该 cookie 的值发送至所有这些站点。浏览器会把 domain 的值与请求的域名做一个尾部比较(即从字符串的尾部开始比较),并将匹配的 cookie 发送至服务器。domain 选项的值必须是发送 Set-Cookie 消息头的主机名的一部分,例如我不能在 google.com 上设置一个 cookie,因为这会产生安全问题。不合法的 domain 选择将直接被忽略。
path选项
另一个控制 Cookie 消息头发送时机的选项是 path 选项,和 domain 选项类似,path 选项指定了请求的资源 URL 中必须存在指定的路径时,才会发送 Cookie 消息头。这个比较通常是将 path 选项的值与请求的 URL 从头开始逐字符比较完成的。如果字符匹配,则发送 Cookie 消息头,例如:
Set-Cookie:name=Nicholas;path=/blog
在这个例子中,path 选项值会与 /blog,/blogtool 等等相匹配;任何以 /blog 开头的选项都是合法的。需要注意的是,只有在 domain 选项核实完毕之后才会对 path 属性进行比较。path 属性的默认值是发送 Set-Cookie 消息头所对应的 URL 中的 path 部分。
secure选项
最后一个选项是 secure。不像其它选项,该选项只是一个标记而没有值。只有当一个请求通过 SSL 或 HTTPS 创建时,包含 secure 选项的 cookie 才能被发送至服务器。这种 cookie 的内容具有很高的价值,如果以纯文本形式传递很有可能被篡改,例如:
Set-Cookie: name=Nicholas; secure
事实上,机密且敏感的信息绝不应该在 cookie 中存储或传输,因为 cookie 的整个机制原本都是不安全的。默认情况下,在 HTTPS 链接上传输的 cookie 都会被自动添加上 secure 选项。
cookie使用失效日期
当 cookie 创建时指定了失效日期,这个失效日期则关联了以 name-domain-path-secure 为标识的 cookie。要改变一个 cookie 的失效日期,你必须指定同样的组合。当改变一个 cookie 的值时,你不必每次都设置失效日期,因为它不是 cookie 标识信息的组成部分。例如:
Set-Cookie:name=Mike;expires=Sat,03 May 2025 17:44:22 GMT
现在已经设置了 cookie 的失效日期,所以下次我想要改变 cookie 的值时,我只需要使用它的名字:
Set-Cookie:name=Matt
cookie 的失效日期并没有改变,因为 cookie 的标识符是相同的。实际上,只有你手工的改变 cookie 的失效日期,否则其失效日期不会改变。这意味着在同一个会话中,一个会话 cookie 可以变成一个持久化 cookie(一个可以在多个会话中存在的),反之则不可。为了要将一个持久化 cookie 变为一个会话 cookie,你必须删除这个持久化 cookie,这只要设置它的失效日期为过去某个时间之后再创建一个同名的会话 cookie 就可以实现。
需要记得的是失效日期是以浏览器运行的电脑上的系统时间为基准进行核实的。没有任何办法来来验证这个系统时间是否和服务器的时间同步,所以当服务器时间和浏览器所处系统时间存在差异时这样的设置会出现错误。
httponly
表示此 Cookie 必须用于 http 或 https 传输。这意味着,浏览器脚本,比如 javascript 中,是不允许访问操作此 Cookie 的。
cookie自动删除
cookie 会被浏览器自动删除,通常存在以下几种原因:
- 会话 cooke (Session cookie) 在会话结束时(浏览器关闭)会被删除。
- 持久化 cookie(Persistent cookie)在到达失效日期时会被删除。
- 如果浏览器中的 cookie 数量达到限制,那么 cookie 会被删除以为新建的 cookie 创建空间。
Cookie的缺陷
- cookie 会被附加在每个 HTTP 请求中,所以无形中增加了流量。
- 由于在 HTTP 请求中的 cookie 是明文传递的,所以安全性成问题。(除非用 HTTPS)
- Cookie 的大小限制在 4KB 左右。对于复杂的存储需求来说是不够用的。
HTTP Cookie总结
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
HTTP Session工作流程总结
- 浏览器第一次请求网站, 服务端生成 Session ID。
- 把生成的 Session ID 保存到服务端存储中。
- 把生成的 Session ID 返回给浏览器,通过 set-cookie。
- 浏览器收到 Session ID, 在下一次发送请求时就会带上这个 Session ID。
- 服务端收到浏览器发来的 Session ID,从 Session 存储中找到用户状态数据,会话建立。
- 此后的请求都会交换这个 Session ID,进行有状态的会话。
传输安全
最后再聊聊传输安全,有一种叫做 Session ID 劫持的,假如 Session ID 是基于 HTTP 协议传输的,因为是明文传输,那么它就可能被中间的路由器劫持。 攻击者得到 Session ID 后,把它带到自己的请求中,就能够进入你的账户。
所以一些 Web 框架还提供了 Session 的一些安全保护,比如间隔时间内动态刷新 Session ID,加上 Token 等
Session和Cookie的区别
- cookie 数据存放在客户端,session 数据放在服务器上。
- cookie 不是很安全,别人可以分析存放在本地的 Cookie 并进行 Cookie 欺骗考虑到安全应当使用 session。
- session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用 Cookie。
- 单个 Cookie 保存的数据长度不能超过 4K,很多浏览器都限制一个网址最多保存 20 个 cookie。
- 将登陆信息等重要信息存放为 SESSION,其他信息如果需要保留,可以放在 COOKIE 中。
- Session 的运行依赖 Session ID,而 Session ID 是存在 Cookie 中的,也就是说,如果浏览器禁用了 Cookie,Session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 Session ID)。
报文分类与格式
所有的 HTTP 报文都可以分为两类:请求报文和响应报文。请求和响应报文的基本报文结构相同。请求报文的格式:
<method> <request-URL> <version>
<headers>
<entity-body>
响应报文的格式(注意,只有起始行的语法有所不同):
<version> <status> <reason-phrase>
<headers>
<entity-body>
HTTP响应报文
一个 http 响应报文也由四个部分组成:
- 状态行(Status-Line)
- 响应头部(Response Header Fields)
- 回车换行(CRLF)
- 消息体(Message Body)
状态行(Status-Line)
HTTP/1.1 200 OK
上面是一个典型的 http 响应状态行,我们可以看到也是由三部分组成的:
- http 协议版本
- 状态码(Status Code)
- 状态码的文本描述(Reason-Phrase)
响应头部(Response Header Fields)
和请求头部类似,就是两者之间有一些不同的头部字段。
消息体(Message Body)
返回给客户端的具体消息。