如今,基于两个令牌的安全模式非常普遍。 Internet上有很多有关主题的信息。 通常仅描述什么是刷新和访问令牌以及如何使用。
为了理解令牌背后的概念,我想做一个简单的思想实验。
假设您是一个喜欢钱的学生,但账户余额通常约为零。
在去大学的路上,您打开银行移动应用程序以检查您的帐户余额。
为了向您显示余额应用程序需要执行对银行服务器的请求:
GET http://api.mybank.com/balance HTTP 1.1
并收到回应
{ balance: '$0.0' }

如果没有人可以检查您的银行帐户,那将很好。 为此,让我们添加带有用户名和唯一密码的特殊标头:
`授权:用户名密码
授权:JohnDwayson QWERTY1`
现在,服务器可以通过您的个人用户名和密码对您进行身份验证。 这意味着,JohnDwayson QWERTY1对必须保存在手机上的秘密位置,以防止他人不允许您花钱。 如果我们可以避免存储密码,但是没有人想输入太多,那就更好了。
当我们在公共汽车上使用我们的移动应用程序30分钟时,它将在30分钟内多次通过网络发送您的密码。

每个需求集都包含密码
让我们继续我们的思想实验。 您仍然在公共汽车上,您的应用程序将请求发送到服务器。
如果欺诈行为在您周围坐着公共汽车,并且他可以拦截从您的手机到银行服务器的一个或几个HTTP数据包,该怎么办?
如果应用程序每分钟更新一次信息,欺诈将有30种可能使请求相交并获取您的密码。
假设他偷了5-6条消息。 现在您的密码已被盗用,欺诈者可以使用它来访问您的帐户信息。

中间的人可以听到密码
他可以用这些信息做什么? 好消息是,他无法花钱给您,因为您还没有钱。:)他也无法更改密码,在这种情况下,您会注意到这一点,并且可以使用银行的SMS来恢复密码。 在这种情况下,旧的被盗密码将不再有效,学生将省钱
但是他能做的就是与您同时检查您的余额。 他可以轻松编写一个Python脚本,该脚本每10秒检查一次您的帐户,一旦您收到付款,他就可以花每一分钱购买比特币。 如果他的剧本每天花费5到10布克,而且甚至可能在几个月或几年内不会引起注意,情况可能更糟。

黑客在等你的钱
为避免这种情况,我们可以要求用户在一段时间后更改密码。 假设密码有效期为3个月。 好消息是,三个月后,他将失去对您帐户的访问权限。 不好的是,他只会在3个月后失去对您帐户的访问权限。 显然,为避免这种情况,我们可以要求您每月或每周更改一次密码。 但这对于最终用户可能会很烦人。
如果我们在请求中不经常使用密码,那就太好了。 为此,我们可以引入特殊的端点: http : //api.mybank.com/login 。 我们将仅将凭据发送到此端点,作为回报,服务器将为我们生成特殊令牌。 让它只是服务器存储在其数据库中的唯一字符串GUID。 我们将此唯一字符串称为Access令牌。 在与服务器进行的每个请求对话的标题中,我们将使用它代替我们的用户名和密码。 与密码类似,让我们设置访问令牌的到期时间。

要更新余额,我们不需要密码
让我们重现我们的思想实验。
我们在公共汽车上,有人偷了我们的http请求以检查余额。 但是现在,欺诈行为仅具有访问令牌。

仅窃取访问令牌是不够的
黑客现在可以使用此信息做什么? 首先,他仍然不能花你的钱。 其次,他不能更改密码,因为他根本不知道密码。 而且,现在密码和访问令牌的到期时间是两个单独的设置。 我们可以将密码的有效期保留为3个月,我们需要选择访问令牌的有效期。 我们希望将此时间设置得尽可能短,因为要获取新的访问令牌,我们不能使用令牌本身。 如果有人窃取了此令牌,他将能够根据需要续签该令牌。
访问令牌过期后,要获取新令牌,我们需要再次发送用户凭据。 如果您在公交车上停留30分钟,并且访问令牌的有效时间为15分钟,则需要至少发送两次密码。 通常,我们希望更早地更新访问令牌,例如每10分钟更新一次,否则用户可能会注意到获取新令牌的一些延迟。 此外,它还解决了由于令牌过期而导致同时有几个请求失败而导致的问题,我们需要在获取新令牌后以正确的顺序重试它们。
现在效果更好。 欺诈仅在10分钟(而不是3个月)内访问我们的数据。 但是他仍然可以窃取我们的密码,而且在他窃取我们的钱之前,我们可能不会注意到这一点。
现在的问题是获取访问令牌,我们仍然需要密码。 如果我们将再生成一个唯一的字符串,该怎么办。 一种特殊令牌,仅用于获取访问令牌。
为此,我们的登录端点应接受用户名和密码,并返回称为“刷新令牌”的新令牌。 我们将存储在我们的移动应用程序中。 刷新令牌的到期时间可能更长,例如一个月。

更新流程以获取刷新和访问令牌
每次我们需要更新访问令牌时,我们只需要发送刷新令牌即可。 因此,我们的密码只会在一个月内发送一次到服务器,甚至很少。 基本上,我们只需要密码即可检索第一个刷新令牌。
我们仍然需要访问令牌来执行请求以从服务器获取一些数据。 因此:
POST /login with username and password returns us refresh token
POST /renew with refresh token returns us access token
POST /balance with access token returns us actual balance
让我们再次重播实验。
我们在坐公共汽车。 我们的学生正在检查他的余额。
但是现在我们的应用程序使用刷新令牌,该令牌存储在手机文件中的某个位置。
应用程序正在发送如下请求:
POST /renew
GET /balance
GET /balance
GET /balance
POST /renew
GET /balance
GET /balance
GET /balance
如前所述,黑客在总线中与访问令牌相交的请求很少。
由于访问令牌的到期时间为15分钟,因此我们每10分钟更新一次令牌。
黑客只能在10分钟或更短的时间内使用被盗的信息。
如果他与/ renew请求之一相交并具有刷新和访问令牌怎么办? 从理论上讲,他可以使用刷新令牌一个月来获取新的访问令牌,并且仍然能够偷钱。
但是,如果我们将在每个续订请求中重新生成两个令牌,并且服务器一次仅存储一个刷新令牌。 欺诈的刷新令牌副本也将在10分钟内失效,并且他不能在不通知我们令牌已被盗用的情况下使用它。 如果他尝试续签令牌,它将使我们的令牌无效,我们将被迫重新登录。
刷新令牌的另一个优点是我们不需要在应用程序或浏览器中存储密码,而只存储最后一个刷新令牌。
有趣的是,访问令牌不仅可以是随机字符串,还可以包含一些有用的信息。 例如,可以是到期时间,用户标识和用户角色。 在这种情况下,它称为自包含令牌,服务器不需要数据库访问即可验证令牌和用户权限。 它可以在微服务中非常有用,因为它可以提高性能并减少微服务之间的耦合。
它有点不符合文章主题,但是值得一提的是scope
。
如果我们仅询问/续订端点仅可用于检查余额的访问令牌怎么办?
如果用户想花一些钱,他应该为此获得其他不同的第二令牌吗? 我们可以将此参数称为“作用域”。
因此,我们可以要求/续订Scope = [balance,news]。 如果我们的黑客仍然使用此令牌,他将无法使用它来花费我们的钱!
与仅设计密码或一个令牌相比,基于两个令牌的思想可以提供更好的安全性。 与选择更长的密码或更强的安全性算法相反。
这是我对令牌背后的基本概念及其工作原理的解释。
在实践中,可以很容易地用谷歌搜索这个概念的几种不同实现。
关于灵活选择密码和令牌的到期时间的小注释。
典型的到期时间可以是:
密码为3-6个月。 最好有这个限制。 由于数据库可能被盗,用户可以跨服务重复使用相同的密码。
对于刷新令牌,到期时间可以约为一周或一个月。 对于在办公室中拥有一些网站的人们来说,这段时间至少要长于周末是很有用的。 否则,他们需要在每个星期一输入密码。
访问令牌的到期时间可以在10到60分钟之间。 时间越短,就需要越多的续约请求,但是时间越长,欺诈的机会就越大。
结论 :
我们需要使用密码来获取长寿的刷新令牌。 然后发送刷新令牌以获得短暂的访问令牌。 比利用访问令牌执行有用的请求。