Cookie

 翻阅了好久关于Cookie的博客及文档,感觉一直有一块结没有解开,所以一直难以在脑中形成一个顺畅的知识脉络。最后实在是遭不住,拉上我的大神朋友在食堂里坐了3个小时,问了个底朝天!总算形成了清晰的知识脉络了。

 学习知识和技术一定要知道

  1. 是什么需求或场景导致了该知识或技术的产生
  2. 该知识或技术的实现原理是什么
  3. 该技术是否有什么缺陷,能否有更好的解决方案或者补救措施

 啊,闲话说太多了,让我们来开始正题吧。

 以一个场景为例:

 你登录网站A的账号,想要在这次输入用户名和密码登录之后,在一段时间内,重新打开该页面的时候,不需要再次输入账号名密码就能够登录账号,并收到该网站给你的推送信息。

 很正常的需求,那么让我们从程序员的角度来看看究竟是如何实现的。

进化之路

 保存用户登录状态的思路是这样一步步进化的:(以下都是假设使用https协议进行通信,也就是就算被数据抓包,攻击者也得不到有效的登录信息)

无状态的纯https通信 -> 每次登录账号都需要重新输入账号密码 -> 服务端使用一个flag变量来判断登录态 -> 服务端使用纯Cookie来判断登录态 -> 服务端使用session配合Cookie来判断登录态。(当然,可能还会有更安全更高效的方法,限于我才疏学浅,待我学习到了再与大家分享!)

无状态的纯https通信

 大家都知道的是,https是无状态的通信协议。

 也就是说,你这次与服务端通信,通信结束了,断开TCP连接,等到下次你再与该服务器进行通信,服务器就完全不认识你了,也就无从谈起登录状态保存了。

 该协议这样设计,确实减小了通信的压力,但对于想要保存登录状态的我们,实在是让人困扰。

每次登录账号都需要重新输入账号密码

 这是我们最不想看到的方式,由于https没有记忆性,所以每次登录我们都需要重新输入账号密码再传输给服务端文件,然后服务端文件根据传过来的账号密码在数据库中检索,若检索到则判断用户登录。

 每次都需要输入账号密码才能登录账号,这当然不行。我们继续。

使用一个flag变量来判断登录态

 第一次或登录态过期后再一次在ajax里带上用户的用户名与密码了,服务端接受到请求后,解析出带过来的数据,然后在数据库中进行搜索,是否存有该用户名及密码,如有,则创建一个变量flag(假设flag值等于1,为登录态有效。为0登录态无效),返回给用户。往后用户再与服务器进行交互时,都带上flag,服务端根据这个flag进行判断登录态是否有效。

 但是,这个方法本质上就是无法执行的。

 因为flag只是一个临时变量,重新打开一个tab页面时临时变量就都会被清空。也就是说,flag无法在客户端做到存储,也就无从谈起每次与服务端交互时都带上这个数据了。

 虽然说,该方式不可行,但其思路是有借鉴的意义的——只要我们能够将flag数据长久地存储在客户端中,那么就能够做到每次与服务端交互都带上这个数据了。

 这个客户端存储工具就是——Cookie。

 可能有的同学就说了:“那我们就想办法不要每次检查登录态都检索一次数据库,这样做——当用户第一次(或者是登录态过期后再一次)输入用户名密码,将这些信息通过ajax传给服务端,服务端接受到后,在数据库里检索,如果检索到了,则生成一个flag发给客户端,往后每次客户端与该服务器通信时都带上这个flag信息,只要该flag信息意义为有效,则用户登录态为有效。”

 非常聪明,这就是“使用纯Cookie来判断登录态”的基本思路了。至于该思路的问题我们接下来讲。

使用纯Cookie来判断登录态

 思路是这样的:

 当用户第一次(或者是登录态过期后再一次)输入用户名密码,将这些信息通过ajax传给服务端,服务端接受到后,在数据库里检索,如果检索到了,则使用set-Cookie生成一个Cookie(为了使服务端不光能够判断该登录态有效且能够知道用户的身份,所以该Cookie里得保存能够表明用户身份的信息,如账号,密码。)发给客户端,往后每次客户端与该服务器通信时都在请求头带上这个Cookie信息,只要该Cookie信息意义为有效,则用户登录态为有效,并返回该用户有关的信息,也就不需要再次输入登录的账号密码。

 这个Cookie的意义就是flag。

 这个思路成功地解决了长久存储flag的问题,不过随之而来了新的问题。

  1. Cookie是一个文本文件,并且直接明文保存在浏览器中,可以直接通过inspect->Application->Cookies(以Chrome浏览器为例)中获得明文的Cookie的信息,若Cookie保存的是保密信息(如银行的登录用户名密码),有人使用你的电脑的话,则会被直接看到了,极其不安全。不过,也可以将cookie值进行加密,这样就算别人看到了你浏览器中的cookie,也无法得到其有效信息
  2. 可以通过js脚本的document.Cookies()来获得Cookie,如果有别有用心的人利用这一点,使用XSS攻击(稍后会讲)的话,那么你的Cookie就会被他们获取到了,你的信息也就完全暴露给了他们。如果你想禁止这个操作,则在服务器设置Set-Cookie请求头时,设置httpOnly为true

 所以说这个思路也是有很大问题的,不过若是你的网站安全要求不高,甚至说没有安全要求,那么用这个思路也是可以的。

使用session配合Cookie来判断登录态

 一步步解决问题,一步步进化,我们终于来到了这一步。不过不是说该方法是完美的,他也有很多问题漏洞,等下就讲。

 既然我不想我的保密信息直接暴露在浏览器中,那么我们就在上一种思路的基础上,修改一个地方——我在Cookie中不直接保存用户的保密信息了,而是将该保密信息存在session(session可以理解为一个对象,每个session都存储在session文件中)中,生成一个session_id(很长的随机字符串)来标识该session,session_id设置在Set-Cookie中。

 我们来捋一遍过程:

 当用户第一次(或者是登录态过期后再一次)输入用户名密码,将这些信息通过ajax传给服务端,服务端接受到后,在数据库里检索,如果检索到了,创建一个session对象,在该对象中保存用户的登录信息,并生成一个session_id来作为该session的标识,再使用set-Cookie生成一个Cookie(Cookie内容为session_id)发给客户端,往后每次客户端与该服务器通信时都在请求头带上这个Cookie信息,只要该Cookie中保存的session_id能够匹配到session,则用户登录态为有效,并返回该用户有关的信息。

 这样的方式虽然解决了Cookie明文存储登录信息的问题(因为客户端中的Cookie保存的是很长的随机字符串,不用担心别人看到你的Cookie内容了就知道你的账号密码是多少了)。

 但还有个问题没有解决——可以通过js脚本的document.Cookies()来获得Cookie。如果你下载的页面中有攻击者嵌入的攻击代码,该代码就能读取到你的Cookie,然后再将得到的Cookie传给攻击者自己的服务器。

 那么,攻击者是如何嵌入攻击代码的呢?该html页面的脚本不都是开发者自己编写的吗,攻击者无法直接修改html文件的内容呀,总不可能开发者自己写了攻击代码攻击自己的网站吧?

 这就涉及到了XSS攻击。

XSS攻击

 在讲XSS攻击之前,我们先提一下同源策略,这是Web安全的一大根基。

同源策略(拒绝对html的dom结构的操作,访问别的浏览器不能带上不同源的cookie,ajax请求不能发送)

 同源策略:限制了从一个源加载的文档或脚本与另一个源的资源进行交互。(同源的意思为:相同协议,域名,端口)

 会禁止如下跨域操作:

  1. js使用ajax请求不同源的脚本文件
  2. js操作不同源的dom
  3. js获得不同源的cookie,localStorage,sessionStorage信息

 但不禁止如下跨域操作:

  1. 使用script,link,img标签时,跨域发起请求(即指定的src即使是不同源的,对应的后端文件也会响应请求)

 例如说不同源的网站A向网站B发起ajax请求,由于两者不同源,网站B会拒绝响应网站A的请求。这样的话,就防止了无权限的恶意请求,也就是说,网站B只响应有权限的请求(即同源文件的请求),而同源文件都是由可信赖的合作人编写的,所以比较安全。

 举个例子:

 攻击者发现了银行修改金额信息只需要使用ajax,在携带的信息中带上修改的金额数,后端文件接收到该请求会根据该数据来修改金额(当然实际情况不可能这么简单),那么攻击者只要从自己的服务器使用对应的url带上金额信息,如果没有同源策略的话,银行的服务器会处理该请求,也就修改了银行的数据库中的金额数据。这显然是不安全的。

 既然这样,那么我们就规定,一个源中的文件只响应同一个源下的请求,从根源上杜绝不安全代码生效。

 不过,世上没有密不透风的墙,你不响应不同源的请求,你只响应同源的请求是吧。那么我就让你同源的文件为我服务,获取到我想要的信息并发给我的服务器。

 这我们就回到了XSS攻击。

 在一般情况下,html的内容都是由开发人员设置的。但如果这是个论坛呢?(只要是能够渲染出用户的输入内容的网站就行)

 是这样的,当你输入发言内容,服务器将你的发言内容存在了数据库中,等到别人再次访问会将你的输入内容渲染出来,如果说正常文本没有关系,但如果是带有script标签呢?那么渲染到页面上的时候,就会被认为是js脚本,并乖乖地执行该脚本,给坏人做事。

 这样,攻击者就能够在正常的html页面中嵌入他的攻击代码。如果我们想要做防范措施的话,就是过滤用户的输入文本,禁止输入非法字符。

 ok,这个时候攻击者在你的html页面嵌入了攻击者的攻击代码,并且你的html页面执行了他的攻击代码帮他做了坏事,由于是同源的,所以该攻击代码相当于拥有了所有权限为所欲为。现在攻击者已经拿到了他想要的信息了,不过他还有个问题要解决——如何才能跨域将数据传输到他的服务器中。(因为攻击者的服务器绝对是跟你的服务器是不同源的,所以必须得解决跨域问题,否则攻击者的服务器接受不到从别的源中攻击得到的数据)

 一个最简单粗暴的方法——在嵌入的攻击代码中,执行插入img标签,并设置其src属性,设置该属性的值为攻击者服务器的域名并带上得到的数据作为url参数。

 因为img标签不受同源策略的限制,所以不同源的文件可以互相响应。

 就这样,用户得到了你的信息,并将数据传到了他的数据库中为他所用。

 归根结底的防范方法就是:过滤输入文本,设置字符黑名单。

 不过这还是有漏洞的,例如说将<设置为黑名单了,但我使用<的转义字符,该转义字符不在黑名单中,还是有漏洞。还是那句话世上没有不透风的墙。

CSRF攻击

 XSS攻击是通过注入攻击代码来攻击获取你的信息权限,而CSRF攻击则是通过直接使用被攻击者的Cookie伪装为被攻击者,也就有了被攻击者的权限,从而进行攻击。

 说的太抽象了,老规矩,举个例子:

 假设你是个学生A,老师是老师B,他是你的大物老师,而你期末考试在他手底下挂了,你想要在正方教育系统中改成绩。但是,改成绩需要老师的账号密码来登陆获取修改权限,而你没有账号密码。但是,没有账号密码没有关系啊,你有他的Cookie就可以了,不就可以伪装成老师,来修改成绩了。

 当然,这个时候你也没有老师B的Cookie,这个时候你想到,如果老师访问了正方教育系统,然后没有退出该页面,即该网站的Cookie还是有效,那么只要老师再在同一个浏览器的不同tab页面再次访问正方教育系统,由于同源,不就会自动在请求头上加上Cookie值了吗?

 所以,只要我们知道正方教育系统对应路由的url,以及参数形式,不就能够做到伪装成老师B,并让服务器做出你想让他做的修改。

 ok,这时候你不知道从哪个渠道知道了正方教育系统修改成绩的url,如:http://域名/xxxx/xxxx/ChangeScore.jsp?id=学号&score=成绩

 这时候,你就自己写个网站,里面有个img,其src就为正方教育系统修改成绩的url,然后你给老师发封邮件,其中就是该网站的链接。只要老师在登录正方教育系统网站的同时,点开了你邮件中的链接,你的网站就会跨域发起GET请求,而又因为请求头中带有正方教育系统给老师B的Cookie(再多嘴一句,Cookie是在同一个浏览器,不同tab页面,访问同一个源时共享的),所以正方教育系统网站后台发现该Cookie的session_id能够找到对应的session,也就验证了该访问用户是权限用户,也就进行了该用户的权限操作,用传过来的score参数修改成绩。

 ok,CSRF攻击的过程就讲完了。

 CSRF攻击,攻击者不需要像XSS攻击一样要拿到用户信息才能攻击,他仅需要的是让用户自己(当然,用户并不知情)去使用Cookie访问目标网站,只是说,攻击的内容由攻击者确定。

 那么,如何防范CSRF呢?

 第一种思路,由于CSRF是自动进行的,被攻击者在不知情的情况下就自动地执行了恶意操作。那么我们在确认执行操作之前,添加一个确认机制呢?例如说验证码,这样不就阻止了自动的CSRF攻击吗?

 第二种思路,CSRF的攻击是无法得到Cookie信息的,那么如果网站要求url中还需要Cookie中保存的一个随机数呢?也就是服务器为每次登录状态生成的随机数,即为Token,会存储在Cookie中返回给用户。

 ok,所以就算是使用session加上cookie也不是万事大吉的,还是有很多漏洞可以钻。

 不过做好相应的防范措施,该方法确实是个不错的方法。

 到这里,已经为大家梳理好了知识脉络了,接下来我会大家稍微介绍一个相关的知识。

关于Cookie的知识

cookie的类型

 我们按该Cookie的过期时间分为两类:会话Cookie和持久Cookie。

 会话Cookie是一种临时型Cookie,存储在内存中,只要用户退出浏览器(不是删除该tab页面),会话Cookie就会被一次性删除。设置会话Cookie的方法是:在创建cookie不设置Expires即可。

 持久Cookie存储在硬盘中,不会因退出浏览器而清除持久Cookie。但他并不是真正永久型Cookie,set-Cookie中设置一个特定的过期时间(expires)或者有效期(Max-Age),当有效期过了,则持久Cookie失效,也就会清除。

Cookie的属性内容

domain

 Cookie是跨域是被禁止的,例如a.b.com的Cookie不可以传输给c.b.com。

 但可以使用domain来做到同个顶域下的跨域传输。

 产生Cookie的服务器可以向set-Cookie响应首部添加一个Domain属性来控制与哪些主机通信时,会在请求头带上该Cookie。

 不过有几点要注意的:

  1. domain的值只能是自己的域或自己的顶域,不能是其他的域。如你不能设置domain值为baidu.com
  2. 不声明domain的话,只有本域才能获得
  3. 如果声明了domain,一般包含子域名

Path

 如果我们不想与该域下的所有路由进行http通信时都带上该Cookie,那么就在set-Cookie中设置对应的Path属性,来指定与该域下的哪个文件通信时,才带上Cookie头

Secure

 在set-Cookie中设置secure时,那么只有是https通信时,才会在请求头中带上Cookie。

HttpOnly(禁止使用JavaScript操作Cookie)

 一般来说,我们可以直接通过document.cookie来获取和设置Cookie值。

 但显然这非常的不安全呢,所以我们通过在set-Cookie中设置HttpOnly来禁止JavaScript操作Cookie。这也是防范XSS攻击的好办法。

Cookie带来的效率问题

 有必要先解释一下什么是静态资源与动态资源:

  • 不由服务器执行的,而是由浏览器执行的就是静态资源(如html,js,css,图片文件)
  • 反之就是动态资源,一般是指的数据库资源

 一个网页中有许多的静态资源,这也是用户要访问的,这样的话,请求每个静态资源都需要在请求头加上cookie,但是,请求静态资源并不需要进行cookie验证,这样也就造成了严重的资源浪费,同时也降低了访问速度。

 所以对应的解决方案是:

多域名拆分。其思想是

 如服务器域名为base.com,那么我们将页面文件放在page.base.com这个主机中,将静态资源文件放在static.base.com这个主机中。

 这样的话,两者的域名不同。当访问static.base.com获取静态资源时,就不会传输page.base.com中的cookie。

关于session的知识

 当距离用户上一次使用session(即重新登录页面)的时间间隔大于了失效时间,服务端就会认为客户端已经停止了活动,就会把session删除以节省空间。

 session是被删除只有如下几种情况:

  1. 超时(一般在30分钟左右)
  2. 程序中主动删除
  3. 程序关闭

 也就是说,不会因为浏览器的关闭(也就是通常说的会话结束)而删除。而之所以大家会有这个错觉,是因为大部分session机制是使用会话Cookie来保存session_id的。

 只要Cookie(客户端维护),session(服务端维护)中有一者失效了,则登录态就失效了。

 SESSION的内容通常是与用户信息相关的信息: 1. 身份信息、登陆状态 2. 用户的个性配置、权限列表 3. 其他的一些通用数据(比如购物车)

 不过使用session+cookie来保存登录态有个问题:

  1. session保存在服务端中,当用户访问量变大时,服务端需要存储大量的session,对于服务器压力很大
  2. 当服务器为一个集群的时候,用户登录一个服务器,会将session保存在这个服务器中,而再次访问网站,可能登录的是另一个服务器,但却无法从该服务器中找到对应的session(因为该session在另一个服务器中存着呢),一般来说我们使用第三方服务器来做到缓存一致,不过不方便

Token

 基于此,也就应运而生了token,token无需在服务端中存储,是用算法来判断登录态,过程如下:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

(全文完)

参考资料:

  1. 浅谈CSRF攻击方式:https://www.cnblogs.com/wangyuyu/p/3388169.html
  2. cookie的用途,它的优点和缺点:https://blog.csdn.net/shuidinaozhongyan/article/details/78242192
  3. Session和Token的区别:https://blog.csdn.net/qq_1290259791/article/details/81193914
  4. 有关什么情况下session会失效:https://blog.csdn.net/czh500/article/details/80211410
  5. 聊一聊 cookie:https://segmentfault.com/a/1190000004556040
  6. 关于cookie的安全性问题:https://segmentfault.com/q/1010000007347730?_ea=1318399
  7. 这一次带你彻底了解Cookie:https://www.cnblogs.com/zhuanzhuanfe/p/8010854.html

Cookie浅析的更多相关文章

  1. 浅析Django之session与cookie

    浅析Django之session与cookie 1 session与cookie概述 原理: 由于HTTP协议是无状态,无连接的,当用户发起网路请求时,需要服务端能标识用户ID,用以存储用户相关信息, ...

  2. 浅析session&cookie

    session&cookie没有出现的黑暗时代 大家都知道,HTTP协议是一种无状态的协议,本次请求下一次请求没有任何的关联,所有没有办法直接用http协议来记住用户的信息,试想一向,每一次点 ...

  3. 浅析angular框架的cookie

    相信接触过网页编程的基本上都知道cookie这个东西吧,一个毫不起眼,但是又十分的重要的东西,今天我们就来分析一下这个小东西,我们都知道客服端通过发送http请求到服务器请求我们的数据,当我们的服务器 ...

  4. 浅析Session和Cookie

    Cookie   Cookie的作用,就是当一个用户通过http访问一个服务器时,这个服务器会将一些key/value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户访问该 ...

  5. 浅析Web数据存储-Cookie、UserData、SessionStorage、WebSqlDatabase

    Cookie 它是标准的客户端浏览器状态保存方式,可能在浏览器诞生不久就有Cookie了,为什么需要Cookie 这个东东?由于HTTP协议没有状态,所以需要一个标志/存储来记录客户浏览器当前的状态, ...

  6. 浅析cookie

    基本概念:cookie是指web浏览器存储的少量数据,该数据会在每次请求一个相关的URL时自动传到服务器中. 以博客园为例,我们看看cookie有哪些属性:  1.Name:cookie的名称: 2. ...

  7. 浅析PHP中cookie与session技术

    1.cookie是什么? cookie指某些站点为了辨别用户身份.进行session跟踪而储存在用户本地终端上的数据(通常经过加密). 通俗来理解就是,你去一个专卖店或者超市买东西,然后店里给你办一张 ...

  8. http协议和web应用有状态和无状态浅析

    http协议和web应用有状态和无状态浅析 (2013-10-14 10:38:06) 转载▼ 标签: it   我们通常说的web应用程序的无状态性的含义是什么呢? 直观的说,“每次的请求都是独立的 ...

  9. (转) 浅析HTML5在移动应用开发中的使用

    (转)浅析HTML5在移动应用开发中的使用 (原)http://www.iteye.com/magazines/67   2012-03-07  来自 UECD.163.com  编辑 wangguo ...

随机推荐

  1. 自定义的Config节点及使用

    示例   下面的代码示例演示如何在创建自定义节时使用 ConfigurationProperty. C# VB   using System; using System.Configuration; ...

  2. cygwin pip安装

    1.概要 当python的包多了以后,你会发现一个个去下载然后安装挺麻烦的,耗时耗力.java里面有maven,ivy来帮你管理jar包,而类似的python里有pip来完成这个任务. 2.pip安装 ...

  3. Pandas 错误笔记(持续更新)

    更新至2018.5.1 字典生成DataFrame 今天一个字典生成一个DataFrame,采用了以下形式,每一个value都是一个数(不是vector) df = pd.DataFrame({ 'i ...

  4. Oracle系列-锁表与解锁解决方案(大招版)-解决问题才是王道

    [Oracle系列-锁表与解锁解决方案(大招版)] --1查看被锁的表 select b.owner,b.object_name,a.session_id,a.locked_mode from v$l ...

  5. XML错误信息Referenced file contains errors (http://www.springframework.org/schema/beans/spring-beans-4.0.xsd). For more information, right click on the message in the Problems View ...

    错误信息:Referenced file contains errors (http://www.springframework.org/schema/beans/spring-beans-4.0.x ...

  6. 大白话5分钟带你走进人工智能-第三节最大似然推导mse损失函数(深度解析最小二乘来源)(1)

                                                    第三节最大似然推导mse损失函数(深度解析最小二乘来源)        在第二节中,我们介绍了高斯分布的 ...

  7. git解析日志常用命令

    git diff --name-only ORIG_HEAD 获取变更列表 git log -p 查看每个提交引入的实际更改. git log --oneline --decorate 查看日志列表 ...

  8. Python一行代码实现快速排序

    上期文章排序算法——(2)Python实现十大常用排序算法为大家介绍了十大常用排序算法的前五种(冒泡.选择.插入.希尔.归并),因为快速排序的重要性,所以今天将单独为大家介绍一下快速排序! 一.算法介 ...

  9. Python爬虫入门这一篇就够了

    何谓爬虫 所谓爬虫,就是按照一定的规则,自动的从网络中抓取信息的程序或者脚本.万维网就像一个巨大的蜘蛛网,我们的爬虫就是上面的一个蜘蛛,不断的去抓取我们需要的信息. 爬虫三要素 抓取 分析 存储 基础 ...

  10. Android进程间通信(一):AIDL使用详解

    一.概述 AIDL是Android Interface Definition Language的缩写,即Android接口定义语言.它是Android的进程间通信比较常用的一种方式. Android中 ...