在一些互联网公司的口试中,口试官每每会问这样一个问题:“如果禁用浏览器 cookie,如何实现用户追踪和认证?”
遗憾的是:依然有大量候选人答非所问,无法搞清楚 cookie 和 session 之间的差异。
而在事情中也有让人惊异的真实案例:把 user ID 存储到 local storage 中当做 token 利用,缘故原由是他们声称弃用了 cookie 这种掉队的东西;一个移动端项目,做事器给出的 API 中须要客户端仿照一个 cookie,从而像浏览器中 ajax 那样消费 API。

互联网是基于 HTTP 协议构建的,而 HTTP 协议由于大略盛行开来,但是 HTTP 协议是无状态(通信层面上虚电路比数据报昂贵太多)的。为此人们为了追踪用户想出了各种办法,包括 :cookie/session 机制、token、flash 跨浏览器 cookie 乃至浏览器指纹等。
把用户身份藏在每一个地方(浏览器指纹技能乃至不须要存储介质)
认证、授权、凭据
首先,认证和授权是两个不同的观点,为了让我们的 API 更加安全和具有清晰的设计,理解认证和授权的不同就非常有必要了,它们在英文中也是不同的单词。
认证:是 authentication,指的是当前用户的身份——当用户上岸过后系统便能追踪到他的身份做出符合相应业务逻辑的操作。
即利用户没有登录,大多数系统也会追踪他的身份,只是当做来宾或者匿名用户来处理。认证技能办理的是 “我是谁?”的问题。
授权:与认证不同,授权是 authorization,指的是什么样的身份被许可访问某些资源,在获取到用户身份后连续检讨用户的权限。
单一的系统授权每每是伴随认证来完成的,但是在开放 API 的多系统构造下,授权可以由不同的系统来完成,例如 OAuth。授权技能是办理“我能做什么?”的问题。
实现认证和授权的根本是须要一种媒介(credentials)来标记访问者的身份或权利,在现实生活中每个人都须要一张身份证才能访问自己的银行账户、结婚息争决养老保险等,这便是认证的凭据。
在古代军事活动中,天子会给出战的将军颁发兵符,下级将领不关心持有兵符的人,只须要实行兵符对应的命令即可。在互联网天下中,做事器为每一个访问者颁发 session ID 存放到 cookie,这便是一种凭据技能。
数字凭据还表现在方方面面:SSH 登录的密匙、JWT 令牌、一次性密码等。
用户账户也不一定是存放在数据库中的一张表,在一些企业 IT 系统中,对账户管理和权限有了更多的哀求。以是,账户技能 (accounting)可以帮助我们利用不同的办法管理用户账户,同时具有不同系统之间共享账户的能力,例如:微软的活动目录(AD),以及大略目录访问协议(LDAP),乃至区块链技能。
还有一个主要的观点是:访问掌握策略(AC)。如果我们须要把资源的权限划分到一个很细的粒度,就不得不考虑用户以何种身份来访问受限的资源,选择基于访问掌握列表(ACL)还是基于用户角色的访问掌握(RBAC)或者其他访问掌握策略。
在盛行的技能和框架中,这些观点都无法伶仃的被实现,因此在现实中利用这些技能时,大家每每为一个 OAuth2 是认证还是授权这种观点辩论不休。
为了随意马虎理解,我在文末附上了一份常见技能和观点的术语表。
下面,我会先容在API开拓中常常利用的几种认证和授权技能:HTTP Basic AUthentication、HAMC、OAuth2,以及凭据技能JWT token。
HTTP Basic Authentication
你一定用过这种办法,但不一定知道它是什么。
在不久之前,当你访问一台家用路由器的管理界面,每每会看到一个浏览器弹出表单,哀求你输入用户密码。
在这背后,当用户输入完用户名密码后,浏览器帮你做了一个非常大略的操作:
组合用户名和密码然后 Base64 编码。给编码后的字符串添加 Basic 前缀,然后设置名称为 Authorization 的 header 头部。API 也可以非常大略的供应 HTTP Basic Authentication 认证办法,那么客户端可以很大略通过 Base64 传输用户名和密码即可:。
将用户名和密码利用冒号连接,例如 :username:abc123456。为了防止用户名或者密码中存在超出 ASCII 码范围的字符,推举利用UTF-8编码。将上面的字符串利用 Base 64 编码,例如:dXNlcm5hbWU6YWJjMTIzNDU2。在 HTTP 要求头中加入 “Basic + 编码后的字符串”——即:Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l。这种办法实现起来非常大略,在大量场景下被采取。
当然缺陷也很明显,Base64 只能称为编码,而不是加密 (实际上,无需配置密匙的客户端并没有任何可靠地加密办法,我们都依赖 TSL 协议)。
这种办法的致命弱点是:编码后的密码,如果明文传输则随意马虎在网络传输中透露,在密码不会过期的情形下,密码一旦透露,只能通过修正密码的办法。
HMAC(AK/SK)认证
在我们对接一些 PASS 平台和支付平台时,会哀求我们预师长西席成一个 access key(AK) 和 secure key(SK),然后通过署名的办法完成认证要求。这种办法可以避免传输 secure key,且大多数情形下署名只许可利用一次,避免了重放攻击。
这种基于 AK/SK 的认证办法紧张是利用散列的认证码 (Hash-based MessageAuthentication Code) 来实现的。因此,有很多地方叫 HMAC 认证,实际上不是非常准确。
HMAC 只是利用带有 key 值的哈希算法天生择要,在设计 API 时有详细不同的实现。
HMAC 在作为网络通信的认证设计中作为凭据天生算法利用,避免了口令等敏感信息在网络中传输。
基本过程如下:
客户端须要在认证做事器中预先设置 access key(AK 或叫 app ID) 和 secure key(SK)。在调用 API 时,客户端须要对参数和 access key 进行自然排序后并利用 secure key 进行署名天生一个额外的参数 digest。做事器根据预先设置的 secure key 进行同样的择要打算,并哀求结果完备同等。把稳 secure key 不能在网络中传输,以及在不受信赖的位置存放(浏览器等)。为了让每一次要求的署名变得独一无二,从而实现重放攻击,我们须要在署名时放入一些滋扰信息。
在业界标准中有两种范例的做法,质疑/应答算法(OCRA: OATH Challenge-Response Algorithm)、基于韶光的一次性密码算法(TOTP:Time-based One-time Password Algorithm)。
1. 质疑/应答算法
质疑/应答算法须要客户端先要求一次做事器,得到一个 401 未认证的返回,并得到一个随机字符串(nonce)。
将 nonce 附加到按照上面说到的方法进行 HMAC 署名,做事器利用预先分配的 nonce 同样进行署名校验,这个 nonce 在做事器只会被利用一次,因此可以供应唯一的择要。
2. 基于韶光的一次性密码认证
为了避免额外的要求来获取 nonce,还有一种算法是利用韶光戳,并且通过同步韶光的办法协商到同等,在一定的韶光窗口内有效(1分钟旁边)。
这里的只是利用韶光戳作为验证的韶光窗口,并不能严格的算作基于韶光的一次性密码算法。
标准的基于韶光的一次性密码算法在两步验证中被大量利用,例如:Google 身份验证器不须要网络通信也能实现验证(但依赖准确的授时做事)。
事理是:客户端做事器共享密钥然后根据韶光窗口能通过 HMAC 算法打算出一个相同的验证码。
TOTP 基本事理和常见厂商
OAuth2 和 Open ID
OAuth(开放授权)是一个开放标准,许可用户授权第三方网站访问他们存储在其余的做事供应者上的信息,而不须要将用户名和密码供应给第三方网站或分享他们数据的所有内容。
OAuth 是一个授权标准,而不是认证标准。供应资源的做事器不须要知道确切的用户身份(session),只须要验证授权做事器付与的权限(token)即可。
上图只是 OAuth 的一个简化流程,OAuth 的基本思路便是通过授权做事器获取 access token 和 refresh token(refresh token 用于重新刷新access token),然后通过 access token 从资源做事器获取数据 。
在特定的场景下还有下面几种模式:
授权码模式(authorization code)简化模式(implicit)密码模式(resource owner password credentials)客户端模式(client credentials)如果须要获取用户的认证信息,OAuth 本身没有定义这部分内容,如果须要识别用户信息,则须要借助其余的认证层,例如: OpenID Connect。
1. 验证 access token
在一些先容OAuth 的博客中,很少讲到:资源做事器是怎么验证 access token 的?
OAuth core 标准并没有定义这部分,不过在 OAuth 其他标准文件中提到两种验证 access token的办法。
1)在完成授权流程后,资源做事器可以利用 OAuth 做事器供应的 Introspection 接口来验证access token,OAuth做事器会返回 access token 的状态以及过期韶光。
在OAuth标准中验证 token 的术语是 Introspection。同时,也须要把稳 access token 是用户和资源做事器之间的凭据,不是资源做事器和授权做事器之间的凭据。资源做事器和授权做事器之间该当利用额外的认证(例如:Basic 认证)。
2)利用 JWT 验证:授权做事器利用私钥签发 JWT 形式的 access token,资源做事器须要利用预先配置的公钥校验 JWT token,并得到 token 状态和一些被包含在 access token 中信息。因此,在 JWT 的方案下,资源做事器和授权做事器不再须要通信,在一些场景下带来巨大的上风。同时,JWT 也有一些弱点,我会在JWT 的部分阐明。
2. refresh token 和 access token
险些所有人刚开始理解 OAuth 时都有一个一疑问:为什么已经有了 access token 还须要 refresh token 呢?
授权做事器会在第一次授官僚求时,一起返回 access token 和refresh token,在后面刷新 access token 时只须要 refresh token。
access token 和 refresh token 的设计意图是不一样的,access token 被设计用来客户端和资源做事器之间交互,而 refresh token 是被设计用来客户端和授权做事器之间交互。
某些授权模式下, access token 须要暴露给浏览器,充当一个资源做事器和浏览器之间的临时会话,浏览器和资源做事器之间不存在署名机制,access token 成为唯一凭据。因此,access token 的过期韶光(TTL)该当只管即便短,从而避免用户的 access token 被嗅探攻击。
由于哀求 access token 韶光很短,refresh token 可以帮助用户掩护一个较永劫光的状态,避免频繁重新授权。
大家会以为让 access token 保持一个长的过期韶光不就可以了吗?
实际上,refresh token 和 access token 的不同之处在于:纵然 refresh token 被截获,系统依然是安全的,客户端拿着 refresh token 去获取 access token 时,同时须要预先配置的 secure key,客户端和授权做事器之前始终存在安全的认证。
3. OAuth、Open ID、OpenID Connect
认证方面的术语实在太多,我在搭建自己的认证做事器或接入第三方认证平台时,有时候到完成开拓事情的末了一刻都无法理解这些术语。
OAuth 卖力办理分布式系统之间的授权问题,纵然有时候客户端和资源做事器或者认证做事器存在同一台机器上。OAuth 没有办理认证的问题,但供应了良好的设计利于和现有的认证系统对接。
Open ID 办理的问题是:分布式系统之间身份认证问题,利用Open ID token 能在多个别系之间验证用户,以及返回用户信息,可以独立利用,与 OAuth 没有关联。
OpenID Connect 办理的是:在 OAuth 这套体系下的用户认证问题,实现的基本事理是将用户的认证信息(ID token)当做资源处理。在 OAuth 框架下完成授权后,再通过 access token 获取用户的身份。
这三个观点之间的关系有点难以理解,用现实场景来说:如果系统中须要一套独立的认证系统,并不须要多系统之间的授权可以直接采取 Open ID。
如果利用了 OAuth 作为授权标准,可以再通过 OpenID Connect 来完成用户的认证。
JWT
在 OAuth 等分布式的认证、授权体系下,对凭据技能有了更多的哀求,比如:包含用户 ID、过期等信息,不须要再外部存储中关联。
因此,业界对 token 做了进一步优化,设计了一种自包含令牌,令牌签发后无需从做事器存储中检讨是否合法,通过解析令牌就能获取令牌的过期、有效等信息,这便是JWT (JSON Web Token)。
JWT 是一种包含令牌(self-contained token),或者叫值令牌 (value token),我们以前利用关联到 session 上的 hash 值被叫做引用令牌(reference token)。
简而言之,一个基本的JWT令牌为一段点分3段式构造。
天生JWT 令牌的流程为:
header json 的 base64 编码为令牌第一部分。payload json 的 base64 编码为令牌第二部分。拼装第一、第二部分编码后的 json 以及 secret 进行署名的令牌的第三部分。因此,只须要署名的 secret key 就能校验 JWT 令牌,如果在体中加入用户 ID、过期信息就可以实现验证令牌是否有效、过期了,无需从数据库/缓存中读取信息。由于利用了加密算法,以是第一、二部分纵然被修正(包括过期信息)也无法通过验证。
JWT 优点是:不仅可以作为 token 利用,同时也可以承载一些必要信息,省去多次查询。
把稳:
JWT token 的第一、二部分只是 base64 编码,肉眼不可读,不应当存放敏感信息。JWT token 的自包含特性,导致了无法被撤回。JWT 的署名算法可以自己拟定,为了便于调试,本地环境可以利用对称加密算法,生产环境建议利用非对称加密算法。JWT token 在微做事的系统中上风特殊突出:多层调用的 API 中可以直接通报 JWT token,利用自包含的能力,可以减少用户信息查询次数;更主要的是,利用非对称的加密办法可以通过在系统等分发密匙的办法验证 JWT token。
当然,OAuth 对 access token 等凭据所选用的技能并没有做出限定,OAuth 并不逼迫利用 JWT,在利用 JWT 自包含特性的上风时,必须考虑到 JWT 撤回困难的问题。在一些对撤回 token 哀求很高的项目中不适宜利用JWT,纵然采取了一些方案实现(whitelist 和 blacklist)也违背了设计 JWT 的初衷。
Cookie 、Token in Cookie、Session Token 依然被利用
在构建 API 时,开拓者会创造我们的认证办法和网页运用有一些不同,除了像 ajax 这种范例的 web 技能外,如果我们希望 API 是无状态的,不推举利用 Cookie。
利用 Cookie 的实质是用户第一次访问时做事器会分配一个 Session ID,后面的要求中客户端都会带上这个 ID 作为当前用户的标志。由于 HTTP 本身是无状态的,Cookie 属于一种内建于浏览器中实现状态的办法。如果我们的 API 是用来给客户端利用的,强行哀求 API 的调用者管理Cookie 也可以完成任务。
在一些遗留或者不是标准的认证明现的项目中,我们依然可以看到这些做法,快速地实现认证。
利用 cookie,例如 web 项目中 ajax 的办法。利用 session ID 或 hash 作为 token,但将 token 放入 header 中通报。将天生的 token (可能是JWT)放入 cookie 通报,利用 HTTPonly 和 Secure 标签保护 token。选择得当的认证办法随着微做事的发展,API 的设计不仅仅是面向 WEB 或者 Mobile APP,还有BFF(Backend for Frontend)和 Domain API 的认证,以及第三方做事的集成。
客户端到做事器之间认证和做事器到做事器之间认证是不同的。
我们把终端用户(Human)参与的通信,叫做 Human-to-machine (H2M),做事器与做事器之间的通信叫做 Machine-to-machine (M2M)。
H2M 的通信须要更高的安全性,M2M 的通信天然比 H2M 安全,因此更多的强调性能,在不同的场合下选择得当的认证技能就显得特殊主要。例如: HTTP Basic Authentication 用来作为 H2M 认证显得有些掉队,但是在 M2M 中被大量利用。
其余值得一提的是:H2M 这种通信办法下,客户端不受掌握,由于无法自主分发密匙,认证通信的安全高度依赖 HTTPS。
从一个宏不雅观的角度看待他们的关系,对我们技能选型非常有帮助。
术语表Browser fingerprinting 通过查询浏览器的代理字符串,屏幕色深,措辞等,然后这些值通过散列函数通报产生指纹,不须要通过 Cookie 就可以识别浏览器。MAC(Message authentication code) 在密码学中,讯息鉴别码,是经由特定算法后产生的一小段资讯,检讨某段讯息的完全性。HOTP(HMAC-based One-time Password algorithm)基于散列验证码的一次性密码算法。Two-step verification 是一种认证方法,利用两种不同的元素,合并在一起,来确认利用者的身份,是多成分验证中的一个特例。OTP (One time password )一次性密码,例如注册邮件和短信中的认证码。
参考文章https://swagger.io/docs/specification/authentication/basic-authentication/
[HMAC: Keyed-Hashing for Message Authentication]( “https://www.ietf.org/rfc/rfc2104.txt “)
HOTP: An HMAC-Based One-Time Password Algorithm
OCRA: OATH Challenge-Response Algorithm
The OAuth 2.0 Authorization Framework
JSON Web Token (JWT)
OAuth 2.0
Internet-Draft Archive for OAuth
作者:ThoughtWorks林宁,微信"大众号:ThoughtWorks洞见
本文由@ThoughtWorks林宁 原创发布于大家都是产品经理,未经许可,禁止转载。
题图来自Unsplash, 基于CC0协议