对于 Token,在很多大型网站中都有所应用,比如 Facebook,Twitter,Google,Github 等等,比起传统的身份验证方法,Token 的扩展性更强,也更安全点,非常适合用在 Web 应用或者移动应用上
1.基于Token 的身份验证方法
使用基于 Token
的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
- 客户端使用用户名跟密码请求登录;
- 服务端收到请求,去验证用户名与密码;
- 验证成功后,服务端会签发一个
Token
,再把这个Token
发送给客户端; - 客户端收到
Token
以后可以把它存储起来,比如放在Cookie
里或者Local Storage
里; - 客户端每次向服务端请求资源的时候需要带着服务端签发的
Token
; - 服务端收到请求,然后去验证客户端请求里面带着的
Token
,如果验证成功,就向客户端返回请求的数据。
2.JWT
实施 Token
验证的方法挺多的,还有一些标准方法,比如 JWT
,读作:jot
,表示:JSON Web Tokens
。JWT
标准的Token
有三个部分:·
1. header
(头部),头部信息主要包括(参数的类型–JWT,签名的算法–HS256)
2. poyload
(负荷),负荷基本就是自己想要存放的信息(因为信息会暴露,不应该在载荷里面加入任何敏感的数据)
3. sign
(签名),签名的作用就是为了防止恶意篡改数据
中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
2.1 Header
Header
部分主要是两部分内容,一个是 Token
的类型,另一个是使用的算法,比如下面类型就是 JWT
,使用的算法是 HS256
。
{
"typ" : "JWT",
"alg" : "HS256"
}
上面的内容要用 Base64
的形式编码一下,所以就变成这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2.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
2.3 signatrue
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
,然后进行验证,通过以后就会返回给客户端想要的资源。
3.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
,只是增大攻击成本而已。