您现在的位置:   首页 >> 新闻中心 >> 数据分析

API:认证、授权、凭证

发布人:www.yunke.ai 发布时间:2021-01-01 182 次浏览

以往讲使用 spring security 等具体技术的资料已经很多了,笔者这篇文章不打算写框架和代码的具体实现,而是会讨论认证和授权的区别。然后会介绍一些被业界广泛采用的技术,**会聊聊:怎么为 API 构建选择合适的认证方式?

在一些互联网公司的面试中,面试官往往会问这样一个问题:“如果禁用浏览器 cookie,如何实现用户追踪和认证?”

遗憾的是:依然有大量候选人答非所问,无法搞清楚 cookie 和 session 之间的区别。

而在工作中也有让人惊讶的真实案例:把 user ID 存储到 local storage 中当做 token 使用,原因是他们声称弃用了 cookie 这种落后的东西;一个移动端项目,服务器给出的 API 中需要客户端模拟一个 cookie,从而像浏览器中 ajax 那样消费 API。

互联网是基于 HTTP 协议构建的,而 HTTP 协议因为简单流行开来,但是 HTTP 协议是无状态(通信层面上虚电路比数据报昂贵太多)的。为此人们为了追踪用户想出了各种办法,包括 :cookie/session 机制、token、flash 跨浏览器 cookie 甚至浏览器指纹等。

细说API – 认证、授权和凭证

把用户身份藏在每一个地方(浏览器指纹技术甚至不需要存储介质)

认证、授权、凭证

首先,认证和授权是两个不同的概念,为了让我们的 API 更加安全和具有清晰的设计,理解认证和授权的不同就非常有必要了,它们在英文中也是不同的单词。

细说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

你一定用过这种方式,但不一定知道它是什么。

在不久之前,当你访问一台家用路由器的管理界面,往往会看到一个浏览器弹出表单,要求你输入用户密码。

细说API – 认证、授权和凭证

在这背后,当用户输入完用户名密码后,浏览器帮你做了一个非常简单的操作:

  1. 组合用户名和密码然后 Base64 编码。
  2. 给编码后的字符串添加 Basic 前缀,然后设置名称为 Authorization 的 header 头部。

细说API – 认证、授权和凭证

API 也可以非常简单的提供 HTTP Basic Authentication 认证方式,那么客户端可以很简单通过 Base64 传输用户名和密码即可:。

  1. 将用户名和密码使用冒号连接,例如 :username:abc123456。
  2. 为了防止用户名或者密码中存在超出 ASCII 码范围的字符,推荐使用UTF-8编码。
  3. 将上面的字符串使用 Base 64 编码,例如:dXNlcm5hbWU6YWJjMTIzNDU2。
  4. 在 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 时有具体不同的实现。

细说API – 认证、授权和凭证

HMAC 在作为网络通信的认证设计中作为凭证生成算法使用,避免了口令等敏感信息在网络中传输。

基本过程如下:

  1. 客户端需要在认证服务器中预先设置 access key(AK 或叫 app ID) 和 secure key(SK)。
  2. 在调用 API 时,客户端需要对参数和 access key 进行自然排序后并使用 secure key 进行签名生成一个额外的参数 digest。
  3. 服务器根据预先设置的 secure key 进行同样的摘要计算,并要求结果完全一致。
  4. 注意 secure key 不能在网络中传输,以及在不受信任的位置存放(浏览器等)。

为了让每一次请求的签名变得独一无二,从而实现重放攻击,我们需要在签名时放入一些干扰信息。

在业界标准中有两种典型的做法,质疑/应答算法(OCRA: OATH Challenge-Response Algorithm)、基于时间的一次性密码算法(TOTP:Time-based One-time Password Algorithm)。