这三者都解决了HTTP协议无状态的问题.

Cookie机制

cookie机制是采用在客户端保持状态的方案.cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。

cookie的内容主要包括:名字、值、过期时间、路径和域。路径与域一起构成cookie的作用范围。

不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie.会话cookie一般不存储在硬盘上而是保存在内存里.

设置了过期时间,浏览器就会把cookie保存在硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里cookie,不同的浏览器有不同的处理方式。

Session 机制

session机制是一种服务器端的机制.
客户端对服务端请求时,服务端会检查请求中是否包含一个session标识( 称为session id ).

  • 如果没有,那么服务端就生成一个随机的session以及和它匹配的session id,并将session id返回给客户端.
  • 如果有,那么服务器就在存储中根据session id 查找到对应的session.

当浏览器禁止Cookie时,可以有两种方法继续传送session id到服务端:

  • 第一种:URL重写(常用),就是把session id直接附加在URL路径的后面。
  • 第二种:表单隐藏字段,将sid写在隐藏的表单中。

Token机制

Token是用户的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

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

每一次请求都需要token。token应该在HTTP的头部发送从而保证了Http请求无状态。我们同样通过设置服务器属性Access-Control-Allow-Origin: ,让服务器能接受到来自所有域的请求。需要主要的是,在ACAO头部标明(designating)时,不得带有像HTTP认证,客户端SSL证书和cookies的证书。

Tokens的优势

1.无状态、可扩展

在客户端存储的Tokens是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载负载均衡器能够将用户信息从一个服务传到其他服务器上。

如果我们将已验证的用户的信息保存在Session中,则每次请求都需要用户向已验证的服务器发送验证信息(称为Session亲和性)。用户量大时,可能会造成

一些拥堵。

但是不要着急。使用tokens之后这些问题都迎刃而解,因为tokens自己hold住了用户的验证信息。

2.安全性

请求中发送token而不再是发送cookie能够防止CSRF(跨站请求伪造)。即使在客户端使用cookie存储token,cookie也仅仅是一个存储机制而不是用于认证。不将信息存储在Session中,让我们少了对session操作。

token是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到token自动失效,token有撤回的操作,通过token revocataion可以使一个特定的token或是一组有相同认证的token无效。

3.可扩展性

Tokens能够创建与其它程序共享权限的程序。例如,能将一个随便的社交帐号和自己的大号(Fackbook或是Twitter)联系起来。当通过服务登录Twitter(我们将这个过程Buffer)时,我们可以将这些Buffer附到Twitter的数据流上(we are allowing Buffer to post to our Twitter stream)。

使用tokens时,可以提供可选的权限给第三方应用程序。当用户想让另一个应用程序访问它们的数据,我们可以通过建立自己的API,得出特殊权限的tokens。

4.多平台跨域

我们提前先来谈论一下CORS(跨域资源共享),对应用程序和服务进行扩展的时候,需要介入各种各种的设备和应用程序。

Having our API just serve data, we can also make the design choice to serve assets from a CDN. This eliminates the issues that CORS brings up after we set a quick header configuration for our application.

只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。

  1. Access-Control-Allow-Origin: *

App示例:

APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为Token,存储到服务器中,并返回Token到APP,以后APP请求时,凡是需要验证的地方都要带上该Token,然后服务器端验证Token,成功返回所需要的结果,失败返回错误信息,让他重新登录。

对于同一个APP同一个手机当前只有一个Token;手机APP会存储一个当前有效的Token。其中服务器上Token设置一个有效期,每次APP请求的时候都验证Token和有效期。

下面这个例子,可以很好的理解:

『给我来份煎饼(token我是你对面摊卖烤冷面的,scope赊账)』『好』
『鸡蛋(token我是你对面摊卖烤冷面的,scope赊账)』『好』
『再加个鸡蛋(token我是你对面摊卖烤冷面的,scope赊账)』『好』

最终得到一份普通煎饼,外加两个鸡蛋……

如果服务器重启或者因为其他理由,服务器端已保存token丢失。那么用户需 要重新登录和认证。

『给我来份煎饼(token我是你对面摊卖烤冷面的)』『那个……我没见过你』

cookie与session的区别

1、cookie数据存放在客户端上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。

session与token的区别

作为身份认证 token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击

Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。Session 认证只是简单的把User 信息存储到Session 里,因为SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 但是如果有了某个User的SID,就相当于拥有该User的全部权利.SID不应该共享给其他网站或第三方.

Token,如果指的是OAuth Token 或类似的机制的话,提供的是 认证 和 授权认证是针对用户,授权是针对App。其目的是让 某App有权利访问 某用户 的信息。这里的 Token是唯一的。不可以转移到其它 App上,也不可以转到其它 用户 上。

会话管理机制中的漏洞

会话管理机制中存在的漏洞主要有两类:

  • 会话令牌生成过程中的薄弱环节
  • 在整个生命周期过程中处理会话令牌的薄弱环节

生成过程的薄弱环节

令牌有一定含义

一些会话令牌通过用户名或者邮箱直接转换而来,或者使用一些基本的信息进行创建.这样就很比较容易构建令牌.

常见的具有含义的令牌有以下信息:

  • 账户用户名
  • 应用程序用来区分账户的数字标识符
  • 用户的姓/名
  • 电子邮件
  • 日期/时间戳
  • 一个递增/递减的数字
  • 客户端的IP地址

令牌也很有可能会经过XOR,Base64,转化ASCII等方式先进行编码的操作,再进而生成令牌.

令牌可预测

令牌可预测,主要是有这三个方面造成:隐含序列,时间依赖,生成的数字随机性不强.

隐含序列:主要是指令牌是通过简单的排列,然后进行编码或者进行十六进制的加减法操作而得到,只要猜测出它的基本方法,就可以进行发现规律.
时间依赖:令牌只根据时间的变换,使得令牌产生不同的伪随机.
随机性不强:指令牌是通过简单的线性同余函数生成,如果正好该函数又是公开的,比如java的java.util.Random函数等.

生命过程中的薄弱环节

在网络上泄露令牌

当网站等以非机密的方式传送会话令牌时,就很有可能会导致令牌被窃听.比如使用非加密的HTTP的方式进行通信.

需要注意的是,有些网站虽然部分页面使用了HTTPS,但是还有部分页面使用HTTP,那么令牌就很有可能会在这些HTTP通信的页面中泄露.

在日志中泄露令牌

主要原因可以是应用程序使用URL查询字符串,而不是使用HTTPCookie或者POST请求作为令牌的传输机制. 比如java web中,会在URL中后面带有 http://xxx.com;jsessionid=xxx ;当这样的URL写进日志或者其他历史记录中,那么sid就很容易被获取.

当Cookie被禁止后,就很容易出现使用URL进行传输令牌.

会话终止易受攻击

有些站点,在用户退出后,它只通过set-Cookie等命令清除客户端的令牌,而服务端的令牌没有被删除.也可能会出现用户退出时,应用程序不与服务器通信,导致服务器什么操作都不做. 这些行为会导致当用户再次提交该令牌时,还能够与服务器通信.

客户端暴露在令牌劫持的风险之中

攻击者可能通过XSS攻击用户,获取到用户的Cookie,获取令牌. 所以cookie中要注意设置HTTPOnly,这样可以减缓XSS攻击.

flask中使用cookie和session

cookies

在Flask中操作cookie,是通过response对象来操作,可以在response返回之前,通过response.set_cookie来设置,这个方法有以下几个参数需要注意:

  • key:设置的cookie的key。
  • value:key对应的value。
  • max_age:改cookie的过期时间,如果不设置,则浏览器关闭后就会自动过期。
  • expires:过期时间,应该是一个datetime类型。
  • domain:该cookie在哪个域名中有效。一般设置子域名,比如cms.example.com。
  • path:该cookie在哪个路径下有效。

使用:

  1. 获取:request.cookies.get(key, '默认值')
  2. 设置:resp.set_cookie(key, value, max_age=整数)
  3. 删除:resp.delete_cookie(key)

session

Flask中的session是通过from flask import session。然后添加值key和value进去即可。

client side session:Flask中的session机制是将session信息加密,然后存储在cookie中。专业术语叫做client side session。

server side session:存储在服务器,客户端保存的时session_id(通过cookie完成)

使用:

  • 获取:session.get(key, ‘默认值’)

设置:

  • session.permanent = True
  • session[key] = value

删除:

  • 指定删除:session.pop(key, None)
  • 清空所有:session.clear()

Token

  1. import time
  2. import base64
  3. import hmac
  4. #生成token 入参:用户id
  5. def generate_token(key, expire=3600):
  6. ts_str = str(time.time() + expire)
  7. ts_byte = ts_str.encode("utf-8")
  8. sha1_tshexstr = hmac.new(key.encode("utf-8"),ts_byte,'sha1').hexdigest()
  9. token = ts_str+':'+sha1_tshexstr
  10. b64_token = base64.urlsafe_b64encode(token.encode("utf-8"))
  11. return b64_token.decode("utf-8")
  12. #验证token 入参:用户id 和 token
  13. def certify_token(key, token):
  14. token_str = base64.urlsafe_b64decode(token).decode('utf-8')
  15. token_list = token_str.split(':')
  16. if len(token_list) != 2:
  17. return False
  18. ts_str = token_list[0]
  19. if float(ts_str) < time.time():
  20. # token expired
  21. return False
  22. known_sha1_tsstr = token_list[1]
  23. sha1 = hmac.new(key.encode("utf-8"),ts_str.encode('utf-8'),'sha1')
  24. calc_sha1_tsstr = sha1.hexdigest()
  25. if calc_sha1_tsstr != known_sha1_tsstr:
  26. # token certification failed
  27. return False
  28. # token certification success
  29. return True

使用方法

  1. token = generate_token(username)