在开拓网络运用时,不管是移动真个 APP 也好,还是 web 端 APP 也好,只要有用户群体存在,都绕不开身份认证这个话题,选择一种好的身份认证方法常常在运用安全中起到了至关主要的浸染。目前用的最多的便是利用“token”认证用户的身份。
本文紧张讲述了你不知道的关于 token 的那些事,以及在目前常见的运用中增加 token 认证的方案。
token 的浸染

token 是什么?大略来说 token 便是在客户端与做事器之间传输的一段字符串。
说到 token 的浸染,那这里不得不提一下 CSRF 攻击。CSRF 全称“跨站要求假造攻击”。假设一个用户登录一个银行网站,此时银行网站将用户的登录状态保存在了浏览器的 cookie 中,每当用户访问这个银行网站的不同页面时,浏览器会自动带上 cookie 中用户的登录状态,做事器以此来判断用户登录与否,并根据用户的登录状态相应不同的结果。
此时,攻击者写了一个恶意页面,内含一个指示银行网站从用户账号向攻击者账号转钱的要求,并诱利用户访问这个攻击者写的恶意页面。一旦用户访问了这个恶意页面,该恶意要求将自动带着 cookie 中用户的登录状态被发送到银行网站的做事器上,银行做事器认为这个要求是用户自己发出的,就实行了该要求,从用户的账号向攻击者的账号转了相应数额的钱,给用户造成了丢失。
CSRF 攻击大体上便是这个样子,不过 CSRF 不是本文讲述的重点,有兴趣可以自行理解 CSRF 的细节部分。
token 便是用来差异要求是来自用户本身还是他人假造的一个好办法。当用户在登录时,做事器天生一个 token 发送给客户端,客户端把这个 token 存在内存中或者本地,每次要求都带上这个 token,做事器吸收到这个 token 并验证合法性,合法即连续实行要求,造孽即拦截要求,不予实行。
由于浏览器的同源策略的限定,攻击者的页面无法跨域得到用户页面吸收到的 token,以是攻击者的要求肯定是无法给出合法的 token 的(打消 token 被盗的可能,token 被盗不是本文谈论的范畴),由此做事器可以判断要求到底是用户自己发出的,还是以用户的名义被假造发出的。从而戒备 CSRF 攻击。
token 在开拓中的实践
1、前后端稠浊开拓
利用前后端稠浊开拓模式是较为传统的开拓模式。一样平常是后端写完功能让前端写样式,前后端共同掩护着同一个页面。在这种运用中,session 会话就挑起了客户端与做事端通信的大旗。要求一样平常以 form 表单的形式发送给做事器。在这种运用中加上 token 进行身份验证常见的有两种方案。
方案一:做事端 token+ 表单页面 token
在用户输入精确的用户名和密码登录成功后,由做事器天生 token,一份存入 session 中,以 PHP 为例:
$_SESSION['token'] =generateToken();
一份存入页面中的表单,在页面上所有的表单中加入一个存放 token 的隐蔽域:
<formaction=\公众\"大众method=\"大众POST\"大众>
...
<inputtype=\"大众hidden\"大众name=\公众token\公众value=\"大众tokenHere\公众>
...
</form>
在表单提交上来时先检讨吸收到的 token 是否与 session 中的 token 相等,相等即可证明要求是来自用户自己,不相等则该要求很可能并非来自用户本身,很可能用户遭到了 CSRF 攻击。
方案二:cookie 中 token+ 表单页面 token
在用户登录成功后做事器天生 token,一份同上存入表单页面的隐蔽域中,一份存入用户 cookie,以 PHP 为例如下:
setcookie(\"大众token\"大众,generateToken(),time()+3600,'','','',true);
同样的,当做事端吸收到要求时,比对 cookie 中的 token 和表单中的 token 是否相等,相等则合法,反之造孽。这种方案的上风在于做事器端可以不存放用户的登录状态,节约了做事器的资源,也算是顺应了 http 的无状态。
2、前后端分离开拓
利用前后端分离的开拓模式是较为新颖的开拓模式。这种开拓模式一样平常是前端与后端先协商好一份 Restful API 文档,标明要求的地址、格式、类型等,然后各自对照着这份 api 文档同时进行开拓,提升了效率。这种开拓模式在目前盛行的单页运用(SPA)中利用较多。在这种运用中可以不该用 session 会话来坚持客户端与做事器的通信。转而只用 JWT(Json Web Token)来实现身份认证。
https://jwt.io/introduction/
单页运用为了掩护其良好的用户体验,发送要求的办法由传统的 form 表单提交改为了利用 AJAX/Fetch 传输数据,实现页面无刷。用户在登录成功吸收到 token 后可以将 token 存在内存中,也便是可以存在一个 JS 全局变量里,也可以存在 LocalStorage 中,唯一的差异是后者可以实现自动登录而前者不可以。每次发送要求时将 base64 编码后的 token 添加到 header 里的 Authorization 中发送给做事器:
//ajax
$.ajax({
type: 'POST',
url: '/api/datapost',
data: {
title: '...',
content: '...'
},
headers: {
Authorization: 'dG9rZW5IZXJl'
},
success: function(res) {
do(res);
}
})
//fetch
fetch('/api/datapost',{
method: 'POST',
headers: {
\"大众Content-Type\"大众: \"大众application/json\"大众,
\"大众Authorization\"大众: 'dG9rZW5IZXJl'
},
body: JSON.stringify(
{
title: '...',
content: '...'
}
)
}).then(res=>do(res))
.catch(e=>handle(e))
做事器解密验证 headers 中的 JWT,得到身份数据。全体流程如下:
大家可以想想为什么前两种方案都须要验证两个 token 是否相等来判断 token 的合法性而这里不须要。
这是由于攻击者如果要利用 CSRF,布局一个包含恶意要求的页面,无论 GET 还是 POST 还是别的要求类型,由于同源策略的限定,要求只能由布局 form 表单发出,AJAX 是不支持跨域发送要求的(除非做事器开启跨域支持,如果做事器开启跨域,开拓者须要严格限定要求的来源,对不信赖的来源不予相应),而通过表单发送的要求是没法添加自定义的 header 头的,也便是说攻击者是发不出 header 中带有 token 的要求,以是我们可以以此来进行身份认证。
这种方案的上风在于做事器保持无状态,不须要坚持用户的登录状态,给做事器节约了资源。而且在一些无法利用 cookie 的场景下也适用。
token 的天生方法
实在 csrf token 便是一段随机值而已,它的实现方法因人而异,不同的公司可能有不同的标准,可以利用标准的 JWT 格式,也可以是内部约定的实现方法,但总的来说要知足随机性,不能轻易被别人预测到;字符数不能太长也不能太短,太短随意马虎被碰撞出来,太长给传输带来不便,耗费资源影响速率。下面分别以 PHP 和 JAVA 为例
PHP:利用 uniqid() 方法天生随机值,开启第二个参数增加一个熵,使天生的结果更具唯一性,应对高并发
functiongenerateToken() {
returnmd5(uniqid(microtime() . mt_rand(),true));
}
JAVA: 利用 java.util 包下面的 UUID 类中的 randomUUID() 方法
publicstaticStringgenerateToken()
{
returnUUID.randomUUID().toString().replace(\"大众-\"大众, \"大众\"大众);
}
当然了,详细的实现方案还要按照各自的业务需求来,这里只是先容了几种常见的 token 方案仅供参考。
转自"大众号:信安之路