基于 Token 的身份验证和安全问题

时间:2021-01-09 04:53:07

1 前言

最近为了学习 Token 知识,作者在网上狂搜资料,其中发现了两篇博文对 Token 的讲解特别详细的,作者看过之后感觉受益匪浅,现将两篇博文整合成一篇博文发布出来,从而分享给大家,希望能够对有需要的童鞋,提供一些帮助。当然,在此也感谢文中提到的两篇博文的原作者的热心分享。

对于 Token,在很多大型网站中都有所应用,比如 Facebook,Twitter,Google,Github 等等,比起传统的身份验证方法,Token 的扩展性更强,也更安全点,非常适合用在 Web 应用或者移动应用上。Token 的中文有人翻译成 “令牌”,我觉得挺好,意思就是,你拿着这个令牌,才能过一些关卡。

2 基于 Token 的身份验证方法

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

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

3 JWT

实施 Token 验证的方法挺多的,还有一些标准方法,比如 JWT,读作:jot ,表示:JSON Web Tokens 。JWT 标准的 Token 有三个部分:

  • header
  • payload
  • signature

中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

3.1 Header

Header 部分主要是两部分内容,一个是 Token 的类型,另一个是使用的算法,比如下面类型就是 JWT,使用的算法是 HS256。

{
"typ" : "JWT",
"alg" : "HS256"
}

上面的内容要用 Base64 的形式编码一下,所以就变成这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

3.2 Payload

Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:

  • iss:Issuer,发行者
  • sub:Subject,主题
  • aud:Audience,观众
  • exp:Expiration time,过期时间
  • nbf:Not before
  • iat:Issued at,发行时间
  • jti:JWT ID

比如下面这个 Payload,用到了 iss 发行人,exp 过期时间,另外还有两个自定义的字段,一个是 name ,还有一个是 admin 。

{
"iss" : "csdn.net",
"exp" : "201511205211314",
"name" : "维C果糖",
"admin" : true
}

使用 Base64 编码以后就变成了这个样子:

eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ

3.3 Signature

JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header 和 payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。

  • header
  • payload
  • secret
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); 
HMACSHA256(encodedString, 'secret');

处理完成以后看起来像这样:

SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

客户端收到这个 Token 以后把它存储下来,下回向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。

4 Web安全

Token,我们称之为“令牌”,其最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。那么,Token 有什么作用?又是什么原理呢?

Token 一般用在两个地方:

  • 防止表单重复提交;
  • Anti CSRF 攻击(跨站点请求伪造)。

两者在原理上都是通过 session token 来实现的。当客户端请求页面时,服务器会生成一个随机数 Token,并且将 Token 放置到 session 当中,然后将 Token 发给客户端(一般通过构造 hidden 表单)。下次客户端提交请求时,Token 会随着表单一起提交到服务器端。

然后,如果应用于“Anti CSRF攻击”,则服务器端会对 Token 值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。不过,如果应用于“防止表单重复提交”,服务器端第一次验证相同过后,会将 session 中的 Token 值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的 Token 没变,但服务器端 session 中 Token 已经改变了。

上面的 session 应用相对安全,但也叫繁琐,同时当多页面多请求时,必须采用多 Token 同时生成的方法,这样占用更多资源,执行效率会降低。因此,也可用 cookie 存储验证信息的方法来代替 session Token。比如,应对“重复提交”时,当第一次提交后便把已经提交的信息写到 cookie 中,当第二次提交时,由于 cookie 已经有提交记录,因此第二次提交会失败。不过,cookie 存储有个致命弱点,如果 cookie 被劫持(XSS 攻击很容易得到用户 cookie),那么又一次 game over,黑客将直接实现 CSRF 攻击。所以,安全和高效相对的,具体问题具体分析吧!

此外,要避免“加 token 但不进行校验”的情况,在 session 中增加了 token,但服务端没有对 token 进行验证,这样根本起不到防范的作用。还需注意的是,对数据库有改动的增、删、改操作,需要加 token 验证,对于查询操作,一定不要加 token,防止攻击者通过查询操作获取 token 进行 CSRF攻击。但并不是这样攻击者就无法获得 token,只是增大攻击成本而已。


名称解释:

[1] XSS 攻击:跨站脚本攻击(Cross Site Scripting),恶意攻击者往 Web 页面里插入恶意 Script 代码,当用户浏览该页之时,嵌入其中 Web 里面的 Script 代码会被执行,从而达到恶意攻击用户的目的。

[2] CSRF 攻击:CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同,XSS 利用站点内的信任用户,而 CSRF 则通过伪装来自受信任用户的请求来利用受信任的网站。与 XSS 攻击相比,CSRF 攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比 XSS 更具危险性。


原文链接:

[1] https://ninghao.net/blog/2834
[2] http://www.cnblogs.com/bukudekong/p/3829875.html