cookie

创建时间:
2014-01-29 01:43
最近更新:
2018-05-31 00:19

Brief

  • cookie 由 W3C 组织提出,最早由 Netscape 社区发展的一种机制。目前 cookie 已经成为标准,所有的主流浏览器如 IE、Netscape、Firefox、Opera 等都支持 cookie。
  • 浏览器向服务器发送 http 请求时 会自动携带 与请求的域匹配的 cookie 而不是所有 cookie。
  • cookie 读写 方面 浏览器的安全性限制: 1. 禁止 读 一级域名 或 顶级域名 不同的站点 写的 cookie。2. 一级域名相同、仅二级或更高级域名不同 的站点,可以通过设置 domain 相互读写 cookie。这种场景可以选择不跨域的 SSO 方案。3. 域名相同、仅协议不同 (https 或 http) 的页面,默认可共享 cookie。
  • 不同的浏览器、不同的操作系统,会把 cookie 文件保存在不同的地方。例如 "IE11 + Windows10" 在 C:\Users\*\AppData\Local\Microsoft\Windows\INetCookies & C:\Users\*\AppData\Local\Microsoft\Windows\INetCookies\Low
  • Windows XP 中,IE 浏览器会在 C:\Documents and Settings\你的用户名\Cookies 文件夹下以文本文件形式保存,一个文本文件保存一个 cookie。
  • 浏览器会将 domainpath 都相同的 cookie 保存在一个文件里,cookie 间用 * 隔开。
  • 由于浏览器每次请求服务器都会携带 cookie,因此 cookie 的内容应该少而精,否则会影响性能。
  • cookie 中保存中文必须编码。一般使用 utf-8 编码。
  • cookie 分为 "会话 cookie" 和 "持久 cookie"。"会话 cookie" 仅存在于浏览器的内存中 而不写 cookie 文件,关闭浏览器时随内存释放而销毁。仅关闭页面、不关闭浏览器 不会销毁 "会话 cookie"。"持久 cookie" 存储在 cookie 文件中。创建 cookie 时 如果不设置 expires 则该 cookie 为 "会话 cookie",反之为 "持久 cookie"。

cookie 设置以及发送过程 的 4 个步骤

  1. 客户端 发送一个 http 请求到 服务器
  2. 服务器 发送一个 http 响应到 客户端,其中包含 set-cookie 头部
  3. 客户端 发送一个 http 请求到 服务器,其中包含 cookie 头部
  4. 服务器 发送一个 http 响应到 客户端

HTML DOM - navigator.cookieEnabled 属性

如果浏览器启用了 cookie 则返回 true,否则返回 false

HTML DOM - document.cookie 属性

document.cookie //该属性是一个可读可写的字符串,可使用该属性对与当前文档相关的所有 cookie 进行读取、创建、修改和删除操作。

Tony 测试记录

document.write(document.cookie) 将显示如 ASPSESSIONIDQSARQATT=AAECDFJDLEPPDPEHEJJAEBKM,估计是24位的Asp Session。

console.log('Tony: 这是动态呈现的 (如无 cookie 则无显示): ' + document.cookie);

domain

  • domain 表示 cookie 所在的域。
  • domain 默认为请求的域。例如请求 www.abc.com/def/test.aspxdomain 默认为 www.abc.com
  • 跨域访问 (示例): www.google.comimg.google.com 被视为两个无关的域名,不能互相操作彼此的 cookie。
  • 跨域访问 (相同根域): 假设 X 的 domainx.abc.com、Y 的 domainy.abc.com。如果要在 X 产生一个令 X 和 Y 都能访问的 cookie 就要将该 cookie 的 domain 设置为 .abc.com (注意 abc 前面的 .); 如果要在 X 生产一个令 X 不能访问而 Y 能访问的 cookie 就要将该 cookie 的 domain 设置为 y.abc.com
  • 跨域访问 (不同根域): 假设 X 的 domainx.com、Y 的 domainy.com。如果要在 X 生产一个令 X 不能访问而 Y 能访问的 cookie 就要将该 cookie 的 domain 设置为 .y.com

path

  • path 表示 cookie 所在的目录。
  • path 默认为 /,即 根目录,此时全站任何路径下均可访问该 cookie。也有网友说 默认为 当前路径,待测试验证。
  • 并不只是创建 cookie 的网页才能读取该 cookie。默认情况下,与创建 cookie 的网页 在同一目录或子目录下的所有网页 都可以读取该 cookie。
  • 假设 在同一个服务器上有以下目录: /test//test/a//test/b/; 假设 cookie0 的 path/test/,cookie1 的 path/test/a/。则 /test/ 下的所有页面都可以访问到 cookie0,而 /test//test/b/ 的子页面不能访问 cookie1。这是因为 cookie 仅允许其 path 路径下的页面访问。
  • /a/test.html 页面创建的 cookie,创建时如果将 path 设置为 /b/,则 该页面自已不能访问该 cookie 而 /b/test.html 页面能该问。
  • 删除 cookie 时注意,删除时 必须带上 创建时相同的 path,否则删除无效。

public string System.Web.HttpCookie.Path { get; set; }

获取或设置要与当前 Cookie 一起传输的虚拟路径。
默认为 /,也就是服务器根目录。
Path 属性扩展 Domain 属性,以完整描述此 Cookie 应用到的特定 URL。 例如,在 URL http:/www.microsoft.com/asp 中,域为 www.microsoft.com,路径为 /asp。

-- http://msdn.microsoft.com/zh-CN/library/system.web.httpcookie.path

secure

  • secure 属性为 true 的 cookie 只会在 https 和 ssl 等安全协议中传输,不会在 http 等非安全协议中传输。
  • secure 属性并不对 cookie 内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对 cookie 内容加密、解密,以防泄密。

编/解码

2012-07-14 Tony 发现 escape/unescape 在 WebStorm 提示 deprecated symbol use,以后向 jQuery.cookie.js 学习使用 encodeURIComponent/decodeURIComponent

Cookie文件名的格式

Tony在Windows7下测试:

开始》运行》cookies,或者在资源浏览器中双击C:\Users\User0\Cookies文件夹,都将提示此位置拒绝访问。
打开百度首页后生成cookie文件Cookie:user0@baidu.com/

网友在其它操作系统下测试:

COOKIE文件为操作系统cookies目录下的一堆txt文件。文件名格式为:
你的用户名@产生的COOKIE的网页文件所在的WEB目录[COOKIE改变的次数].txt
例如:同一个域下可能有多个cookie文件:
elf@sohu[1].txt
elf@sohu[2].txt
elf@sohu[3].txt
文件名中的数字含义不明。
实际上,浏览网页时,浏览器会根据cookies目录下index.dat中的索引信息去定位到某一个文件,然后查找相应COOKIE字段值。

Cookie文件的格式

COOKIE文件为UNIX格式,只有换行(0x0A)没有回车(0x0D)。
每个COOKIE文件内,每个字段均包含8行信息,各字段之间以*分隔:
由于记事本对Unix换行标记不兼容,打开后内容全在一行看起来不方便,
用VS或EditPlus或UltraEdit-32打开即可看到正确的换行。

Tony测试:打开百度首页后生成cookie文件Cookie:user0@baidu.com/,内容如下:

BAIDUID
C97B87EBD1F5E06083C47B29FBC993AC:FG=1
baidu.com/
1024
1514266496
30256674
3805209840
30183248
*

每一行的内容说明:

第一行:Cookie变量名
第二行:Cookie变量值
第三行:该Cookie变量所属域,形如csdn.net/、blog.csdn.net/或blog.csdn.net/lixianlin/
第四行:可选标志
第五行:该Cookie过期时间(FILETIME格式)的高位整数
第六行:该Cookie过期时间(FILETIME格式)的低位整数
第七行:该Cookie创建时间(FILETIME格式)的高位整数
第八行:该Cookie创建时间(FILETIME格式)的低位整数
第九行:Cookie记录分隔符(为一个星号* )

第三行中Cookie变量所属域:
例如:
csdn.net/,它是一个根域,也就是一级域,表示该Cookie变量在该根域下的所有目录中的网页都有效,
不管访问该域下的哪个目录中的网页,浏览器都会将该Cookie信息附在网页头部信息当中发送给服务端;
例如:
blog.csdn.net/,是一个二级域,表示该Cookie只对blog这个二级域下目录中的网页有效;
例如:
blog.csdn.net/lixianlin/,是一个二级域下的目录,只有访问blog这个二级域下lixianlin这个目录中的网页时,才会把该Cookie信息附在请求头部信息当中发送给服务端。
注意:
csdn.net/和www.csdn.net/并不相同,前者是根域,后者是一个二级域,
只是人们习惯了www这样的形式,所以大多数的网站首页都用http://www.xxx.com/这样的二级域来访问。

JavaScript 操作 cookie

  1. JavaScript Cookie - JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 cookie。(仅此一页,这一页也就讲完了,就是这么简单)

document.cookie将返回以分号隔开的多个名/值对所组成的字符串,这些名/值对包括了该域名下的所有cookie。
如果写cookie时,无expires,则此cookie是“会话cookie”,即:此cookie仅在内在中,未写入硬盘文件,浏览器关闭时将丢失。

写一对:
document.cookie='name=value';
写多对(用分号加空格来分隔):
document.cookie='name=value; name=value';

注意:在cookie的名或值中不能使用分号、逗号、等号、空格。所以通常需要编码后写入:
document.cookie=escape('name')+'='+escape('value');
当使用escape()编码后,在取出值以后需要使用unescape()进行解码才能得到原来的cookie值。

写入时,如存在同name的cookie,则覆盖。相当于修改操作。

document.cookie='name=value;path=/';//表示当前cookie在整个网站下可用
document.cookie='name=value;path=/Product';//表示当前cookie仅能在Product目录下使用
document.cookie='name=value;domain=.google.com';//表示google.com所有的二级域名下都可以访问该cookie

JavaScript 读写 cookie 示例

<script>
   var now = new Date();
   now.setDate(now.getDate() + 1);
   document.cookie = 'userName=' + escape('哈哈') + ';expires=' + now.toString() + ';path=/; domain=.baidu.com';
   document.write('cookie 文件中的内容为: ', unescape(document.cookie));
</script>

JavaScript 操作 cookie - Tony的通用方法

//new Date()与Date()均返回当前日期与时间。document.write输出如:Fri Oct 21 03:31:43 2011
//var d=new Date();d.setTime(millisecond);document.write(d);输出如:Thu Jan 1 08:00:00 UTC+0800 1970
//new Date().getTime()返回1970-01-01至今的毫秒数
//toGMTString建议替代为toUTCString,两者格式同为:Thu, 20 Oct 2011 19:24:30 UTC

//Cookie Begin
function CookieGet(name) {
    if(!name)return null;
    if(!document.cookie)return null;
    var array=document.cookie.split(';');
    for(var i=0;i<array.length;i++)
    {
        var array1=array[i].split('=');
        if(array1[0]==name&&array1[1])return unescape(array1[1]);
    }
    return null;
}

function CookieSet(name,value,expiresDay,path,domain,secure) {
    if(!name)return;
    if(!value){document.cookie=name+'=;expires='+Expires(-1);return;}
    var s=name+'='+escape(value);
    if(expiresDay)s+=';expires='+Expires(expiresDay);
    if(path)s+=';path='+path;
    if(domain)s+=';domain='+domain;
    if(secure)s+=';secure';
    document.cookie=s;
}

function Expires(day) {
    var d=new Date();
    d.setTime(d.getTime()+day*24*60*60*1000);
    return d.toUTCString();
}
//Cookie End

JavaScript 清除全部 cookie

以下代码 2018-05-31 备份自网络,待测试:

(function clearAllCookie() {
    var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
    if(!keys) {
        return;
    }
    for(var i = keys.length; i--;) {
        document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString();
    }
})();

About T.Utility.Cookie

Cookie (2012-07-15 Tony 已编写好 T.Utility.Cookie() 及其测试页面)

在http://localhost/CommerceSg/z/aspx0.aspx页面,
通过JavaScript写cookie时,如未设置path,path将默认为/CommerceSg/z;
通过C#写cookie时,如未设置path,path将默认为/。
为统一,Tony在T.Cookie中为path增加了默认值。

通过JavaScript写cookie时,设置path值为
/CommerceSg/z

/CommerceSg/z/
是不一样的。

通过JavaScript写cookie时,name区分大小写。

测试:AJAX请求往返均传递cookie

JavaScript代码:

function F(){
    var ck = T.Utility.Cookie("CkTest");
    ck = T.Type.IsIntegerNonNegative(ck) === true ? Number(ck) + 1 : 0;
    T.Utility.Cookie("CkTest", ck)
    $.ajax({
        success:function(){
            alert(T.Utility.Cookie("CkTest"));
        }
    });
}

服务器代码:

protected void Page_Load(object sender, EventArgs e)
{
    if(T.Cmn.IsAjaxRequest())
    {
        string ckTest = T.Ck.Get("CkTest");
        int integer = ckTest == null ? 0 : (int.Parse(ckTest) + 1);
        T.Ck.Set("CkTest", integer.ToString(), null, false, null, null, false);
    }
}

测试结果:
在浏览器中,多次调用F(),将依次弹窗显示1、3、5……
这演示了AJAX请求与普通请求一样,往返均传递cookie。

各浏览器 标签/窗口 测试共享session/cookie

2012-08-10 Tony测试发现:
在IE9.0.8112.16421/CM20.0.1132.57m/FF14.0.1中,新标签页,甚至新窗口,均共享session/cookie,包括会话cookie与设置了expires的cookie。这导致:只要登录1次,在session/cookie过期前,之后打开的任何标签/窗口均共享登录。而在TT4.8(1000)中,同一窗口的所有标签可共享,不同的窗口不能共享。

cookie 注入典型步骤

Source: web 攻防系列教程之 cookie 注入攻防实战

以下步骤可确定一个网站是否存在 cookie 注入漏洞:

  1. 寻找形如 "abc.asp?id=123" 的 url。
  2. 在浏览器的控制台中执行 document.cookie='id=' + escape('123'),然后访问 abc.asp,如果正常,则说明代码中是用 request('id') 这种方式来获取数据。
  3. 在浏览器的控制台中分别执行:

document.cookie='id=' + escape('123 and 1=1')
document.cookie='id=' + escape('123 and 1=2')
和常规 SQL 注入一样,如果分别返回正常和不正常页面,则说明该应用存在注入漏洞,可以进行 cookie 注入。

  1. 使用常规注入语句进行注入即可。

跨域读写 cookie

  • 一个 跨域读写 cookie 的方案: 在 b.com 公开一个函数,a.com 调用之,例如 a.com 中的 <script src="b.com/setCookie()"/> 把要写进 cookies 信息带上,这样就会把 cookie 信息写进 b.com 域名下。
  • 如何跨域获取 cookie - 因浏览器出于安全目的禁止跨域读写 cookie,故所有跨域读写 cookie 的方案都是 hack

MSDN

  1. ASP.NET 状态管理概述
  2. ASP.NET Cookie 概述

Resource

  1. http://github.com/cloudcome/jquery.cookie/ - Simple to operate cookie. jquery.cookie. 尤雨溪
  2. cookie 劫持
  3. 超大 cookie 拒绝服务攻击
  4. 理解 cookie 和 session 机制