一、 什么是Token
Token原始的意思是“令牌”,是服务端生成的一个自定义字符串,作为客户端进行数据请求的一个标识。在区块链兴起后,Token被赋予“代币”或“通证”的含义,意思是一种可流通的加密数字权益证明。
二、 Token的起源
早在计算机网络刚兴起时,Token就应用在网络通讯中,它本身是一个网络术语。
在以太网成为局域网的普遍协议之前,IBM 曾经推过一个局域网协议,叫做Token Ring Network,令牌环网。网络中的每一个节点轮流传递一个令牌,只有拿到令牌的节点才能通讯。这个令牌,其实就是一种权利,或者说权益证明。
互联网时代,随着Web API和物联网的兴起,基于Token的身份认证机制越来越被大家广泛采用。通常情况下,在Web领域,Token主要用于身份认证和防止表单重复提交。在Web领域基于Token的身份验证随处可见,在大多数使用Web API的互联网公司中,Token是多用户下处理认证的最佳方式。
区块链时代,以太坊及智能合约概念的出现,赋予了Token更广泛的含义。在区块链提到的Token,其实大多是基于以太坊ERC-20标准的一种智能合约产物。基于以太坊ERC-20这个标准,任何人都可以在以太坊上发行自定义的Token,这个Token可以代表任何权益和价值。
三、 Token身份验证原理
在了解Token身份验证原理前,需要先理解传统身份验证方法,才能更透彻的理解相关概念。
3.1 传统身份认证
由于HTTP协议是一种无状态的协议,默认情况下服务端并不知道是谁访问了自己,这就意味着客户端的每次请求都需要进行验证,从而辨别客户端的身份。业界通常的解决方案是通过在服务端存储登录信息(如存储到服务端Session里)来辨别请求的。其业务流程如下:
1) 用户输入登陆凭据;
2) 服务器验证凭据是否正确,并创建会话(Session),然后把会话数据存储在数据库中(分布式会话);
3) 具有会话id的cookie被放置在用户浏览器中;
4) 在后续请求中,服务器会根据数据库验证会话id,如果验证通过,则继续处理;
5) 一旦用户登出,服务端和客户端同时销毁该会话。
流程说明:
当客户端进行登录请求的时候,如果没有问题,在服务端生成一条记录,在这个记录里可以说明登录的用户是谁,然后把这条记录的id发送给客户端,客户端收到以后把这个id存储在cookie里,下次该用户再次向服务端发送请求的时候,可以带上这个cookie,这样服务端会验证一下cookie里的信息,看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。
暴露的问题:
>Seesion:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加;
>可扩展性:在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题,增加服务器并不能解决Session,不过可以采用分布式Session方案,如使用数据库存储Session,每次请求被转向到不同服务器时,可以直接到数据库中查找对应Session;
>CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可能会出现禁止请求的情况;
>CSRF(跨站请求伪造):用户在访问敏感网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。
3.2 基于Token的身份认证
使用Token机制的身份验证方法,在服务器端不需要存储用户的登录记录。大概的流程:
1) 客户端使用用户名和密码请求登录;
2) 服务端收到请求,验证用户名和密码;
3) 验证成功后,服务端会生成一个Token,然后把这个Token发送给客户端;
4) 客户端收到Token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里;
5) 客户端每次向服务端发送请求的时候都需要带上服务端发给的Token;
6) 服务端收到请求,然后去验证客户端请求里面带的Token(实时计算),如果验证成功,就向客户端返回请求的数据。
当用户第一次登录后,服务器生成一个Token并将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
3.3 两种认证方式对比
从流程上来看,两者没有什么本质区别,唯一的区别在于服务端是否存储用户状态。
说明:
实时性比较
在进行身份验证时,传统身份认证方式使用客户端Cookie数据与服务端缓存中Session数据进行对比,在单台服务器时速度非常快,多台时需要其它机制(如数据库)保障Session的同步,效率会有下降。而Token方式认证,由于无状态导致需要在服务端进行计算对比,比有状态单机认证方式稍微差些。
综上所述,比起传统的身份验证方法,基于Token的认证扩展性更强,也更安全,非常适合用在Web应用或者移动应用上。
3.4 基于Token认证的实现方式
Token验证的方法挺多的,相关组织提供了一些标准方法。
1) JWT
读作:jot,表示:JSON Web Tokens。
JWT是一种安全标准,一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息信息的合法性;如果验证成功,会产生并返回一个Token(令牌),用户可以使用这个Token访问服务器上受保护的资源。
根据维基百科的定义,JWT是一种基于JSON的、用于在网络上声明某种主张的令牌(Token)。JWT实际上就是一个字符串,通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。
JWT的实现流程与3.2中流程一致。
具体参见:
https://www.jianshu.com/p/168d34aab2e3
2) OAuth
OAuth,表示:Open Authorization。
OAuth是一个关于授权的开放网络标准,它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动,它允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth的流程如下:
(A)用户打开客户端以后,客户端要求用户给予授权;
(B)用户同意给予客户端授权;
(C)客户端使用上一步获得的授权,向认证服务器申请令牌;
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌;
(E)客户端使用令牌,向资源服务器申请获取资源;
(F)资源服务器确认令牌无误,同意向客户端开放资源。
具体参见:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
四、 Token算法定义
一个Token就是一些用户信息的集合,在Token中若包含足够多的信息,在后续请求中将会减少查询数据库的几率。
4.1 参考案例
首先,我们来看看微信公众号的Token机制。
详见:https://blog.youkuaiyun.com/u014236541/article/details/49795247
4.2 内容组成元素
4.3 加密算法
对包含的内容进行加密后输出一个Token,一般常见的处理方式是先对内容进行Base64编码,然后通过加密算法(如使用JWT的HS256算法,使用SHA1算法)。
4.4 请求方式
客户端在请求时,将Cookie中存储的Token值通过Cookie、Http Header、Body里面提交上来,具体如何传递Token需要看业务场景。比如网页,可以直接通过Cookie;移动端应用可以在Http Header中,TV-STB上可在Http Body中通过Json传递。
客户端在请求时,可参考微信公众号的模式,请求Url中应包括时间戳、签名、随机码,签名应是通过时间戳+Token+随机码进行加密后获得。当然,如果安全级别要求不高,可直接传Token到后端进行验证。
4.5 验证方式
不同的加密算法,以及是否采用无状态模式,都决定了服务端的验证方式。
如果采用有状态模式,则在服务端可以通过Redis存储Token及过期时间,在验证时进行解密及解码;如果无状态模式,则需要客户端提供足够信息由服务端实时计算后进行验证。
五、 安全策略
从安全角度来说,Token并不能解决所有风险。由于Token是必须存储到本地的,所以Token被泄露的机会很高。
在使用Token机制进行身份认证、操作鉴权的过程中,需要注意以下事项:
1) Token时效性
服务器应给Token设置一个有效期,每次客户端请求的时候都验证Token和有效期;
提供对某个Token主动过期的机制,如果Token被盗,有办法及时将有效期内的Token失效。
2) Token的存储
Token可以存到数据库中,但是有可能查询Token的时间会过长导致Token丢失(其实Token丢失了再重新认证一个就好,但是别丢太频繁,别让用户没事儿就去认证)。
为了避免查询时间过长,可以将Token放到内存中。这样查询速度绝对就不是问题了,也不用太担心占据内存,就算Token是一个32位的字符串,应用的用户量在百万级或者千万级,也是占不了多少内存的。
3) Token的加密
Token是很容易泄露的,如果不进行加密处理,很容易被恶意拷贝并用来登录。
在本地存储的时候把Token进行对称加密存储,提交到服务端的时候再由服务端按规则解密。常见的方式为:将请求URL、时间戳、Token三者合并加盐签名,通过算法进行加密处理。
另外在请求时,可要求提供签名,签名使用Token的前几位以哈希算法压缩成的一定长度的十六进制字符串。
4) SSL协议请求
在网络层面上Token使用明文传输的话是非常危险的,所以一定要使用HTTPS协议。
最后需要 特别指出的:风险控制是一个长期的斗智斗勇的过程,不能指望一个解决方案解决所有安全问题。
Token原始的意思是“令牌”,是服务端生成的一个自定义字符串,作为客户端进行数据请求的一个标识。在区块链兴起后,Token被赋予“代币”或“通证”的含义,意思是一种可流通的加密数字权益证明。
二、 Token的起源
早在计算机网络刚兴起时,Token就应用在网络通讯中,它本身是一个网络术语。
在以太网成为局域网的普遍协议之前,IBM 曾经推过一个局域网协议,叫做Token Ring Network,令牌环网。网络中的每一个节点轮流传递一个令牌,只有拿到令牌的节点才能通讯。这个令牌,其实就是一种权利,或者说权益证明。
互联网时代,随着Web API和物联网的兴起,基于Token的身份认证机制越来越被大家广泛采用。通常情况下,在Web领域,Token主要用于身份认证和防止表单重复提交。在Web领域基于Token的身份验证随处可见,在大多数使用Web API的互联网公司中,Token是多用户下处理认证的最佳方式。
区块链时代,以太坊及智能合约概念的出现,赋予了Token更广泛的含义。在区块链提到的Token,其实大多是基于以太坊ERC-20标准的一种智能合约产物。基于以太坊ERC-20这个标准,任何人都可以在以太坊上发行自定义的Token,这个Token可以代表任何权益和价值。
名词解释: 以太坊(英语:Ethereum)是一个开源的有智能合约功能的公共区块链平台。截止2018年2月,以太币是市值第二高的加密货币,仅次于比特币。 智能合约可以理解为在区块链上可以自动执行的(由事件驱动的)、以代码形式编写的合同(特殊的交易)。 |
在了解Token身份验证原理前,需要先理解传统身份验证方法,才能更透彻的理解相关概念。
3.1 传统身份认证
由于HTTP协议是一种无状态的协议,默认情况下服务端并不知道是谁访问了自己,这就意味着客户端的每次请求都需要进行验证,从而辨别客户端的身份。业界通常的解决方案是通过在服务端存储登录信息(如存储到服务端Session里)来辨别请求的。其业务流程如下:
1) 用户输入登陆凭据;
2) 服务器验证凭据是否正确,并创建会话(Session),然后把会话数据存储在数据库中(分布式会话);
3) 具有会话id的cookie被放置在用户浏览器中;
4) 在后续请求中,服务器会根据数据库验证会话id,如果验证通过,则继续处理;
5) 一旦用户登出,服务端和客户端同时销毁该会话。
流程说明:
当客户端进行登录请求的时候,如果没有问题,在服务端生成一条记录,在这个记录里可以说明登录的用户是谁,然后把这条记录的id发送给客户端,客户端收到以后把这个id存储在cookie里,下次该用户再次向服务端发送请求的时候,可以带上这个cookie,这样服务端会验证一下cookie里的信息,看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。
暴露的问题:
>Seesion:每次认证用户发起请求时,服务器需要去创建一个记录来存储信息。当越来越多的用户发请求时,内存的开销也会不断增加;
>可扩展性:在服务端的内存中使用Seesion存储登录信息,伴随而来的是可扩展性问题,增加服务器并不能解决Session,不过可以采用分布式Session方案,如使用数据库存储Session,每次请求被转向到不同服务器时,可以直接到数据库中查找对应Session;
>CORS(跨域资源共享):当我们需要让数据跨多台移动设备上使用时,跨域资源的共享会是一个让人头疼的问题。在使用Ajax抓取另一个域的资源,就可能会出现禁止请求的情况;
>CSRF(跨站请求伪造):用户在访问敏感网站时,他们很容易受到跨站请求伪造的攻击,并且能够被利用其访问其他的网站。
3.2 基于Token的身份认证
使用Token机制的身份验证方法,在服务器端不需要存储用户的登录记录。大概的流程:
1) 客户端使用用户名和密码请求登录;
2) 服务端收到请求,验证用户名和密码;
3) 验证成功后,服务端会生成一个Token,然后把这个Token发送给客户端;
4) 客户端收到Token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里;
5) 客户端每次向服务端发送请求的时候都需要带上服务端发给的Token;
6) 服务端收到请求,然后去验证客户端请求里面带的Token(实时计算),如果验证成功,就向客户端返回请求的数据。
当用户第一次登录后,服务器生成一个Token并将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
3.3 两种认证方式对比
交互流程对比
序号 | 交互项 | 传统身份认证 | 基于Token身份认证 |
1 | 提交用户名密码 | 需要 | 需要 |
2 | 服务端验证 | 需要 | 需要 |
3 | 服务端状态存储 | 需要 | 不需要 |
4 | 本地存储 | 需要 | 需要 |
5 | 服务端鉴权 | 需要 | 需要 |
能力对比
序号 | 功能项 | 传统身份认证 | 基于Token身份认证 |
1 | 实时性 | 高 | 稍差 |
2 | 时效限制 | 有 | 有 |
3 | 跨域 | 不支持 | 支持 |
4 | 跨程序 | 不支持 | 支持 |
5 | 移动应用 | 支持 | 支持 |
6 | 多端联动 | 不支持 | 支持 |
7 | 服务端状态 | 有状态 | 无状态 |
8 | 扩展性 | 差 | 好 |
9 | 安全性 | 低 | 高 |
实时性比较
在进行身份验证时,传统身份认证方式使用客户端Cookie数据与服务端缓存中Session数据进行对比,在单台服务器时速度非常快,多台时需要其它机制(如数据库)保障Session的同步,效率会有下降。而Token方式认证,由于无状态导致需要在服务端进行计算对比,比有状态单机认证方式稍微差些。
综上所述,比起传统的身份验证方法,基于Token的认证扩展性更强,也更安全,非常适合用在Web应用或者移动应用上。
何为无状态、有状态? 服务器所维护的与客户交互活动的信息称为状态信息。不保存任何状态信息的服务器称为无状态服务器(stateless server),反之则称为有状态服务器(stateful server)。 |
Token验证的方法挺多的,相关组织提供了一些标准方法。
1) JWT
读作:jot,表示:JSON Web Tokens。
JWT是一种安全标准,一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交信息信息的合法性;如果验证成功,会产生并返回一个Token(令牌),用户可以使用这个Token访问服务器上受保护的资源。
根据维基百科的定义,JWT是一种基于JSON的、用于在网络上声明某种主张的令牌(Token)。JWT实际上就是一个字符串,通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。
JWT的实现流程与3.2中流程一致。
具体参见:
https://www.jianshu.com/p/168d34aab2e3
2) OAuth
OAuth,表示:Open Authorization。
OAuth是一个关于授权的开放网络标准,它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动,它允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth的流程如下:
(A)用户打开客户端以后,客户端要求用户给予授权;
(B)用户同意给予客户端授权;
(C)客户端使用上一步获得的授权,向认证服务器申请令牌;
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌;
(E)客户端使用令牌,向资源服务器申请获取资源;
(F)资源服务器确认令牌无误,同意向客户端开放资源。
具体参见:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
四、 Token算法定义
一个Token就是一些用户信息的集合,在Token中若包含足够多的信息,在后续请求中将会减少查询数据库的几率。
4.1 参考案例
首先,我们来看看微信公众号的Token机制。
微信后台在向公众账号服务器发送数据的时候,会额外带上4个参数:timestamp、signature、nonce、echostr,如表3-1所示。其中timestamp是时间戳,nonce是一个随机数,signature是对timestamp、nonce和Token进行SHA1加密后的字符串。SHA1的加密过程是不可逆的,即不能通过timestamp、signature和nonce计算出Token是什么。 在公众账号服务器收到timestamp、signature和nonce之后,同样对nonce、timestamp和Token使用SHA1加密算法,得到自己的签名,如果自己的签名和请求中的signatrue是一样的,那么说明请求是来自微信后台而不是恶意第三方。 注意,恶意的第三方有可能会截获微信后台发过来的timestamp、signature和nonce这三个参数,然后直接利用这个三个参数对公众账号服务器发起请求。按照上面的逻辑可以知道,服务器是无法判断出这是个恶意的请求的。这种攻击称为replay攻击。这种攻击方式的防御方法很简单:加上对timestamp的校验。在收到请求之后,将请求包中的timestamp与当前时间比较,如果误差大于一定的值,就可认为这个请求是恶意的。这里不能做相等的比较,因为数据在网络上传输需要时间,同时各个服务的本地时间也是有一些差异的。 微信服务器通过检验signature对请求进行校验(下面有校验方式)。若此次GET的Token验证可以通过,则表示该请求来自微信服务器,这时会原样返回echostr参数内容,表示接入生效,否则接入失败。 |
4.2 内容组成元素
参照微信公众号处理模式,在设计Token时,一般建议包含以下内容:
序号 | 键值 | 说明 |
1 | UID | 用户编号 |
2 | PID | 服务提供者编号 |
3 | Key | 密钥 |
4 | SrcTag | 来源平台 |
5 | SrcIP | 原始请求IP |
6 | TimeStamp | 时间戳,Token创建时间 |
7 | ExpireTime | 过期时间 |
对包含的内容进行加密后输出一个Token,一般常见的处理方式是先对内容进行Base64编码,然后通过加密算法(如使用JWT的HS256算法,使用SHA1算法)。
4.4 请求方式
客户端在请求时,将Cookie中存储的Token值通过Cookie、Http Header、Body里面提交上来,具体如何传递Token需要看业务场景。比如网页,可以直接通过Cookie;移动端应用可以在Http Header中,TV-STB上可在Http Body中通过Json传递。
客户端在请求时,可参考微信公众号的模式,请求Url中应包括时间戳、签名、随机码,签名应是通过时间戳+Token+随机码进行加密后获得。当然,如果安全级别要求不高,可直接传Token到后端进行验证。
4.5 验证方式
不同的加密算法,以及是否采用无状态模式,都决定了服务端的验证方式。
如果采用有状态模式,则在服务端可以通过Redis存储Token及过期时间,在验证时进行解密及解码;如果无状态模式,则需要客户端提供足够信息由服务端实时计算后进行验证。
五、 安全策略
从安全角度来说,Token并不能解决所有风险。由于Token是必须存储到本地的,所以Token被泄露的机会很高。
在使用Token机制进行身份认证、操作鉴权的过程中,需要注意以下事项:
1) Token时效性
服务器应给Token设置一个有效期,每次客户端请求的时候都验证Token和有效期;
提供对某个Token主动过期的机制,如果Token被盗,有办法及时将有效期内的Token失效。
2) Token的存储
Token可以存到数据库中,但是有可能查询Token的时间会过长导致Token丢失(其实Token丢失了再重新认证一个就好,但是别丢太频繁,别让用户没事儿就去认证)。
为了避免查询时间过长,可以将Token放到内存中。这样查询速度绝对就不是问题了,也不用太担心占据内存,就算Token是一个32位的字符串,应用的用户量在百万级或者千万级,也是占不了多少内存的。
3) Token的加密
Token是很容易泄露的,如果不进行加密处理,很容易被恶意拷贝并用来登录。
在本地存储的时候把Token进行对称加密存储,提交到服务端的时候再由服务端按规则解密。常见的方式为:将请求URL、时间戳、Token三者合并加盐签名,通过算法进行加密处理。
另外在请求时,可要求提供签名,签名使用Token的前几位以哈希算法压缩成的一定长度的十六进制字符串。
4) SSL协议请求
在网络层面上Token使用明文传输的话是非常危险的,所以一定要使用HTTPS协议。
最后需要 特别指出的:风险控制是一个长期的斗智斗勇的过程,不能指望一个解决方案解决所有安全问题。