Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OAuth: How and Why?

OAuth: How and Why?

LI Daobing

July 01, 2010
Tweet

More Decks by LI Daobing

Other Decks in Programming

Transcript

  1. 标准历史 •2007-10: OAuth 1.0 •2009-06: OAuth 1.0a ◦主要解决一个安全漏洞 (后面有详细介绍) •2010-04:

    RFC 5849 ◦名词变更, consumer->client, ... ◦时间戳不要求递增 ◦小修改
  2. OAuth 词汇表 •三方 ◦客户端: client, 原称 consumer ◦服务端: server, 原称

    service provider ◦资源所有者: resource owner, 原称 user (本文仍使用"用 户") •三凭证 (凭证即两个随机字符串, 分别为 key 和 secret) ◦客户端凭证: client credentials, 原称 consumer key and secret, 客户端在服务端注册后获得 ◦临时凭证: temporary credentials, 原称 request token and secret ◦令牌凭证: token credentials, 原称 access token and scret
  3. 请求流程 用户 客户端 服务端 1. 发起授权操作 2. 申请临时凭证 3. 返回临时凭证

    4. 跳转/弹出 登录页面 5. 完成授权 6. 跳转至客户端页面 7. 访问客户端页面 8. 申请令牌凭证 9. 返回令牌凭证 客户端凭证 10. 发起 API 请求 11. 返回 API 结果
  4. 详细数据流 (1/5) •缩写 CK: 客户端凭证key CS: 客户端凭证secret TK, TS: 临时凭证

    key 和 secret AK, AS: 令牌凭证 key 和 secret (A 代表 access token)
  5. 详细数据流 (2/5) 0. 初始状态, 客户端持有 CK 和 CS 1. 用户发起授权操作:

    用户点击位于客户端上的一个按钮, 发起授权 2. 客户端申请临时凭证: 客户端向服务端发送 CK 和申请的授权(rights), 并用 CS 签名 3. 服务端返回临时凭证, 服务端首先校验签名, 服务端生成一对 TK 和 TS, 把 {TK: [TS, CK, rights]} 放 入 cache, 把 TK 和 TS 发送到客户端 4. 跳转至登录页面 客户端把 {TK: [TS, client_user]} 放入 cache, 使用 TK 和回调URL (oauth_callback) 构造出登录 URL, 发送一个 301 响应让用户跳转至登录 URL
  6. 详细数据流 (3/5) 5. 用户访问登录URL, URL中包含 TK 和 oauth_callback (回调URL) 6.

    完成授权 如果用户没有登录, 服务端首先引导用户登录, 获得 server_user. 服务端根据 URL 中的 TK 提取出 CK 和 rights, 展示客户端的详细信息以及申请的授权。用户 点击同意后, 服务端产生 AK, AS 以及一个 verify_code (一个随机字符串), 更新 cache 为 {TK: [TS, CK, rights, AK, AS, verify_code, server_user]}, 根据URL中 的 oauth_callback 和 verify_code 构造出新的回调 URL。
  7. 详细数据流 (4/5) 7. 用户访问回调URL, 该URL 中包含 TK 和 verify_code 8.

    客户端申请令牌凭证 客户端通过 TK 拿到 TS 和 user_id, 首先判定此时的用户与申请TK时用户一 致, 然后向服务器发送 CK+TK+verify_code, 并用 CS+TS 签名 9. 服务端返回令牌凭证 服务端通过 TK 从 cache 取出 [TS, CK, rights, AK, AS, verify_code], 校验 CK 一致, verify_code 一致, 签名正确, 销毁 cache, 持久化 {AK: [AS, CK, user_id, rights]}, 返回 AK 和 AS
  8. 详细数据流 (5/5) 10. 客户端发起 API 请求 根据 API 文档准备数据, 加上

    CK, AK, 使用 CS+AS 签名 11. 服务端返回 API 结果 根据 AK 从数据库中调出 [AS, CK, user_id, rights], 校验 CK 及签名, 检验访问的 API 是否在授权范围内, 使用 user_id 对应的数据支持 API, 返回结果到客户端
  9. 请求流程 (补上每步发送的数据) 用户 客户端 服务端 1. 发起授权操作 2. 申请临时凭证 3.

    返回临时凭证 4. 跳转/弹出 登录页面 5. 完成授权 6. 跳转至客户端页面 7. 访问客户端页面 8. 申请令牌凭证 9. 返回令牌凭证 客户端凭证 10. 发起 API 请求 11. 返回 API 结果 CK, rights || CS TK, TS TK, oauth_callback TK, oauth_callback, server_user TK, verify_code TK, verfify_code, client_user client_user CK, TK, verify_code || CS+TS AK, AS CK, AK, ... || CS+AS ...
  10. 签名范例 GET /photos?file=vacation.jpg&size=original HTTP/1.1 #请求 Path Host: photos.example.net #请求 Host

    Authorization: OAuth realm="Photos", oauth_consumer_key="dpf43f3p2l4k3l03", #CK oauth_token="nnch734d00sl2jdk", #TK/AK oauth_signature_method="HMAC-SHA1", #签名方法 oauth_timestamp="137131202", #时间戳 oauth_nonce="chapoH", #nonce oauth_signature="MdpQcU8iPSUjWoN%2FUDMsK2sui9I%3D" #签名 1. 从客户端发送到服务端的每个请求都需要签名 2. 除了前面讲到的数据外, 需要额外加上 oauth_timestamp(时间戳) 以及 oauth_nonce, 用于防止重放攻击 (后面有详解) 3. oauth_signature_method 用于指定签名方法, oauth_signature 为签名 签名细节参见 RFC 5849 3.4 节 , 简单来说就是把请求的内容拼接为字符串 (包括 GET/POST), 然后使用对应的密钥签名
  11. 需求回顾 •我想授权B访问我的数据,同时不暴露我的用户名和密码 用户名和密码都是在 A 服务器上输入的, 从未暴露 •这种授权可以随时回收 在 A 服务器上随时可以回收

    •如果信息被窃听,数据不会有危险 API 请求需要用 CK+AK 来签名, 数据窃听只能听到 AK, 所以 无法伪造请求 (PS, 尽管新的标准要求在返回 TS 和 AS 必须 使用 HTTPS, 但仍有大量服务器使用 HTTP) •如果有恶意中间人,数据不会有危险 理由基本同上, 不过恶意中间人能够给客户端返回假数据,误 导客户端,但仍无法伪造出对服务端的合法请求
  12. 更多的安全细节 1. 客户端需要保存好 TS 2. TS 在服务端是明码保存的, 所以如果服务端被入侵, TS 可能

    会泄漏 3. 服务端用户登录页面必须是 HTTPS 4. 服务端展示客户端凭证密码的页面必须是 HTTPS 页面(很多 现有服务不满足) 5. 服务端返回临时凭证和令牌凭证时必须是 HTTPS 协议(原始 OAuth 协议为应该, RFC 改为必须) 6. 凭证的密钥应当足够长(大部分服务使用 128bit 密钥, douban 的是 64bit) 7. OAuth 不保证 API中涉及到的保密数据的安全。
  13. 本地客户端的 前面介绍的是网站客户端的情况,本地客户端跟网站主要有如下 几点差异 •没有回调 URL 的概念, 服务端直接在用户授权后显示对应的 verify_code, 本地客户端提供一个输入框让用户来输入 •本地客户端需要一个保存CS(客户端凭证密钥)的方案,防止被

    恶意用户获取到, 当然也可以不在本地保存,直接从网上获取 (注意这步保密)。 •如果使用嵌入网页的方法来让用户登录, 用户无法判定浏览器 是安全的,会减弱对你的客户端的信任。
  14. 为什么不用 HTTPS Q: 前面提到的很多手段都是为了应对窃听和恶意中间人, 全部用 HTTPS 不就行了么? •一般情况下, 一个 IP+端口只能部署一套

    HTTPS 密钥,也就是 说没法玩虚拟主机了(因为在 Host 发送前TLS连接就必须建立好 )。 •SNI 扩展支持 HTTPS 虚拟主机,但客户端方面 IE6 不支持, WinXP 下 IE所有版本, Chrome, Safari 不支持. •Dreamhost 之类的 Host 供应商不支持 HTTPS 虚拟主机。 •HTTPS 对服务器端压力也更大 •HTTPS 密钥很昂贵, 比较便宜的godaddy 每年 30 美金左右, 贵的可达数千美金一年
  15. 为什么使用 HMAC-SHA1 Q: HMAC-SHA1 需要计算两次 SHA1, 有必要么? A: 签名是为了防止窃听者和恶意中间人伪造请求, 而其他更简单的签名

    方法都存在安全风险 1. 比如使用 sign = SHA1(key+data), 中间人截获了 data 和 sign, 由于 SHA1 算法的特性,对于构造出 data' = data + padding, 可以计算出 对应的 sign' = SHA1(key+data'), 伪造成功。 2. 比如使用 sign = SHA1(data+key), 中间人截获了 data 和 sign, 如果 我们能找到 data' 满足 SHA1(data)==SHA1(data'), 那么伪造请求 data', sign 也能通过服务端的验证 3. 对于 HMAC: sign = SHA1(f1(key)+SHA1(f2(key)+data)), 没有前面 所说的两种风险。 4. 第二步 SHA1 的参数很短(320bit), 所以对比单次 SHA1, 时间不是两 倍,而只是增加了一个很小的常数时间
  16. 为什么需要 nonce 和 timestamp A: 防止重放攻击 (Replay attack) •一种重放攻击是把截获到的请求重发一次或多次, 对于

    POST 这种非幂等的方法会造成服务器端的数据错误。加入 nonce 后, 首次之后的请求会被拒绝。这种攻击手法只需要窃听能力。 •另一种重放攻击是把请求拦截下来, 不发给服务器。然后等待 到合适的时间后再把请求发给服务器。也可以收集多个请求,重 新组合成合适的顺序后再发送。这种攻击手法需要中间人能 力。 •由于 nonce 与 timestamp 是一起使用的,所以 timestamp 过 期后 nonce 也可以抛弃, nonce 储存容量不会无限制增长。
  17. 1.0a 修复了什么Bug? A: 1.0 下存在如下的一种攻击手法 攻击者 受害人 客户端 服务端 1.

    登录并发起 OAuth 请求 2. 申请临时凭证 3. 下发登录URL 4. 转发登录URL 5. 打开URL, 并完成授权 7. 打开 callback, 绑定成功 6. 打开 callback, 但通常会失败
  18. 1.0a 修复了什么Bug? A: 1.0 下存在如下的一种攻击手法 1. 存在两个用户: 攻击者和受害人, 存在一个客户端,这个客户端 会把用户在服务端的权限通过

    OAuth 绑定到本地帐号。 2. 攻击者登录客户端, 在客户端触发 OAuth 请求, 跳转到服务端 后,不进行授权操作,保存此时的 URL。 3. 攻击者把保存好的 URL 发给受害人, 并诱导受害人完成授权 操作(注意: 此时绑定操作通常会失败, 因为因为临时凭证的密 钥部分应该只存在攻击者的 session 数据中)。 4. 攻击者访问 callback URL, 就可以绑定受害人的权限到攻击者 的帐号。
  19. OAuth 不做什么? •只保护服务端,不保护客户端 ◦OAuth 只保证用户在服务端的数据不被非法访问或修改, 但 不保证客户端接收到正确的数据,恶意中间人可以修改返回 的数据,当然这个也可以用签名来解决。 •不保护恶意浏览器/恶意操作系统攻击 用户必须自行确认自己的浏览器/操作系统是否安全。

    •OAuth 的安全建立在 HTTPS 之上, 攻击 HTTPS 的手段也会 影响到 OAuth ◦比如 DNS 污染加上伪造证书 ◦比如 SSL 2.0 漏洞 •不保护笨蛋, 用户需要教育 OAuth 授权时需要验证确实在真实的服务器上, 避免被恶意客 户端钓鱼
  20. 国内现有 OAuth 服务器的问题 •Douban ◦仍在使用 OAuth 1.0 ◦客户端令牌展示页面没有加密 ◦密钥只有 64bit

    •Sohu 微博 ◦每次认证时都要求用户输入密码, 容易造成用户麻痹, 更容 易被钓鱼网站袭击 •... •PS. 仅代表当时(2010-07)的情况, 不代表现状