OAuth2.0深入理解
OAuth简介
- OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
- OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。
- OAuth(俗称1.0)是OpenID的一个补充,但是完全不同的服务。1.0协议相较于2.0协议复杂些,易用性差,所以没有得到广泛普及。
OAuth和OpenID的区别:
- OAuth关注的是authorization;而OpenID侧重的是authentication。从表面上看,这两个英文单词很容易混淆,但实际上,它们的含义有本质的区别:
- authorization: n. 授权,认可;批准,委任
- authentication: n. 证明;鉴定;证实
- OAuth关注的是授权,即:“用户能做什么”;而OpenID关注的是证明,即:“用户是谁”。
Oauth2.0与Oauth1.0的区别
- 1.0授权分3步:
A)客户端到授权服务器请求一个授权令牌(request token&secret)
B)引导用户到授权服务器请求授权
C)用访问令牌到授权服务器换取访问令牌(access token&secret)
D)用访问令牌去访问得到授权的资源
- 2.0的用户授权过程有2步:
A)引导用户到授权服务器,请求用户授权,用户授权后返回 授权码(Authorization Code)
B)客户端由授权码到授权服务器换取访问令牌(access token)
C)用访问令牌去访问得到授权的资源
- 1.0协议每个token都有一个加密(非对称加密),2.0要求使用https协议,安全性也更高一筹。
- 1.0只有一个用户授权流程,而2.0充分考虑了客户端的各种子态,并且提供了多种途径获取访问令牌,如下:
a)授权码
b)客户端私有证书
c)资源拥有者密码证书
d)刷新令牌
e)断言证书
OAuth 认证的流程
OAuth协议案例描述
协议的参与者,OAuth 的参与实体至少有如下三个:
- RO (resource owner): 资源所有者,对资源具有授权能力的用户。
- RS (resource server): 资源服务器,它存储资源,并处理对资源的访问请求。如 Google 资源服务器,它所保管的资源就是用户的照片等。
- Client: 第三方应用,它获得 RO 的授权后便可以去访问 RO 的资源。如网易印像服务。
此外,为了支持开放授权功能以及更好地描述开放授权协议,OAuth 引入了第四个参与实体:
- AS (authorization server): 授权服务器,它认证 RO 的身份,为 RO 提供授权审批流程,并最终颁发授权令牌 (Access Token)。请注意,为了便于协议的描述,这里只是在逻辑上把 AS 与 RS 区分开来;在物理上,AS 与 RS 的功能可以由同一个服务器来提供服务。
授权类型
在开放授权中,第三方应用 (Client) 可能是一个 Web 站点,也可能是在浏览器中运行的一段 JavaScript 代码,还可能是安装在本地的一个应用程序。这些第三方应用都有各自的安全特性。对于 Web 站点来说,它与 RO 浏览器是分离的,它可以自己保存协议中的敏感数据,这些密钥可以不暴露给 RO;对于 JavaScript 代码和本地安全的应用程序来说,它本来就运行在 RO 的浏览器中,RO 是可以访问到 Client 在协议中的敏感数据。
OAuth 为了支持这些不同类型的第三方应用,提出了多种授权类型,如:
- 授权码 (Authorization Code Grant)
- 隐式授权 (Implicit Grant)
- RO 凭证授权 (Resource Owner Password Credentials Grant)
- Client 凭证授权 (Client Credentials Grant)
其中最核心、最难理解、也是最广泛使用的一种授权类型为 “授权码”。
授权码类型的开放授权协议的流程
- Client 初始化协议的执行流程。首先通过 HTTP 302 来重定向 RO 用户代理到 AS。Client 在 redirect_uri 中应包含如下参数:client_id,scope (描述被访问的资源),redirect_uri (即 Client 的 URI),state (用于抵制 CSRF 攻击)。此外,请求中还可以包含 access_type 和 approval_prompt 参数。当 approval_prompt=force 时,AS 将提供交互页面,要求 RO 必须显式地批准 (或拒绝) Client 的此次请求。如果没有 approval_prompt 参数,则默认为 RO 批准此次请求。当 access_type=offline 时,AS 将在颁发 access_token 时,同时还会颁发一个 refresh_token。因为 access_token 的有效期较短 (如 3600 秒),为了优化协议执行流程,offline 方式将允许 Client 直接持 refresh_token 来换取一个新的 access_token。
- AS 认证 RO 身份,并提供页面供 RO 决定是否批准或拒绝 Client 的此次请求 (当 approval_prompt=force 时)。
- 若请求被批准,AS 使用步骤 1 中 Client 提供的 redirect_uri 重定向 RO 用户代理到 Client。redirect_uri 须包含 authorization_code,以及步骤 1 中 Client 提供的 state。若请求被拒绝,AS 将通过 redirect_uri 返回相应的错误信息。
- Client 拿 authorization_code 去访问 AS 以交换所需的 access_token。Client 请求信息中应包含用于认证 Client 身份所需的认证数据,以及上一步请求 authorization_code 时所用的 redirect_uri。
- AS 在收到 authorization_code 时需要验证 Client 的身份,并验证收到的 redirect_uri 与第 3 步请求 authorization_code 时所使用的 redirect_uri 相匹配。如果验证通过,AS 将返回 access_token,以及 refresh_token (若 access_type=offline)。
步骤 4 中,Client 需要拿 “授权码” 去换 “授权令牌” 时,Client 需要向 AS 证明自己的身份,即证明自己就是步骤 2 中 Alice 批准授权时的 Grantee。这个身份证明的方法主要有两种:
通过 https 直接将 client_secret 发送给 AS
,因为 client_secret 是由 Client 与 AS 所共享,所以只要传送 client_secret 的信道安全即可。(利用TSL安全信道传送client_secret验证身份)通过消息认证码来认证 Client 身份
,典型的算法有 HMAC-SHA1。在这种方式下,Client 无需传送 client_secret,只需发送消息请求的 signature 即可。由于不需要向 AS 传递敏感数据,所以它只需要使用 http 即可。(利用client与AS共享的client_cecret与消息的绑定的hash签名值验证身份)
此外,在步骤 2 中,Google 授权服务器需要认证 Alice 的 RO 身份,并提供授权界面给 Alice 进行授权审批。
为何引入 authorization_code
协议设计中,为什么要使用 authorization_code 来交换 access_token?这是读者容易想到的一个问题。也就是说,在协议的第 3 步,为什么不直接将 access_token 通过重定向方式返回给 Client 呢?如果直接返回 access_token,协议将变得更加简洁,而且少一次 Client 与 AS 之间的交互,性能也更优。那为何不这么设计呢?协议文档中并没有给出这样设计的理由,但也不难分析:
- 浏览器的 redirect_uri 是一个不安全信道,此方式不适合于传递敏感数据 (如 access_token)。
因为 uri 可能通过 HTTP referrer 被传递给其它恶意站点,也可能存在于浏览器 cacher 或 log 文件中,这就给攻击者盗取 access_token 带来了很多机会
。另外,此协议也不应该假设 RO 用户代理的行为是可信赖的,因为 RO 的浏览器可能早已被攻击者植入了跨站脚本用来监听 access_token
。因此,access_token 通过 RO 的用户代理传递给 Client,会显著扩大 access_token 被泄露的风险。但 authorization_code 可以通过 redirect_uri 方式来传递,是因为 authorization_code 并不像 access_token 一样敏感。即使 authorization_code 被泄露,攻击者也无法直接拿到 access_token,因为拿 authorization_code 去交换 access_token 是需要验证 Client 的真实身份。也就是说,除了 Client 之外,其他人拿 authorization_code 是没有用的。此外,access_token 应该只颁发给 Client 使用,其他任何主体 (包括 RO) 都不应该获取 access_token。协议的设计应能保证 Client 是唯一有能力获取 access_token 的主体。引入 authorization_code 之后,便可以保证 Client 是 access_token 的唯一持有人。当然,Client 也是唯一的有义务需要保护 access_token 不被泄露。 - 引入 authorization_code 还会带来如下的好处。由于协议需要验证 Client 的身份,如果不引入 authorization_code,这个 Client 的身份认证只能通过第 1 步的 redirect_uri 来传递。同样由于 redirect_uri 是一个不安全信道,这就额外要求 Client 必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。引入 authorization_code 之后,AS 可以直接对 Client 进行身份认证 (见步骤 4 和 5),而且可以支持任意的 Client 认证方式 (比如,简单地直接将 Client 端密钥发送给 AS)。
消息认证码算法及原理
- MAC(Message Authentication Code,消息认证码算法)是含有密钥的散列函数算法,兼容了MD和SHA算法的特性,并在此基础上加入了密钥。
- MAC算法主要集合了MD和SHA两大系列消息摘要算法。MD系列的算法有HmacMD2、HmacMD4、HmacMD5三种算法;SHA系列的算法有HmacSHA1、HmacSHA224、HmacSHA256、HmacSHA384.HmacSHA512五种算法。
-
MAC函数
- 消息认证码:密码学中,通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具。安全性依赖于Hash函数,故也称带密钥的Hash函数。==消息认证码是基于密钥和消息摘要【hash】所获得的一个值,目的是用于==验证消息的完整性,确认数据在传送和存储过程中未受到主动攻击。
基于 Web 安全的考虑
OAuth 协议设计不同于简单的网络安全协议的设计,因为 OAuth 需要考虑各种 Web 攻击,比如 CSRF (Cross-Site Request Forgery)、XSS (Cross Site Script)、Clickjacking。要理解这些攻击原理,读者需要对浏览器安全 (eg, Same Origin Policy, 同源策略) 有基本理解。
比如,在 redirect_uri 中引入 state 参数就是从浏览器安全角度考虑的,有了它就可以抵制 CSRF 攻击。如果没有这个参数,攻击者便可以在 redirect_uri 中注入攻击者提供的 authorization_code 或 access_token,结果可能导致 Client 访问错误的资源 (比如,将款项汇到一个错误的帐号)。
以上参考:
OAuth (Open Authorization, 开放授权), OpenID
MAC_Message Authentication Code_消息认证码算法
图解Browser端访问OAuth2 API的安全性问题与解决方案