背景
为什么需要OAuth
授权呢?
最典型的应用场景就是第三方登录了, 我们开发了一个网站希望用户可以QQ登录, 但是怎么能拿到用户的 QQ 信息呢? 用户将 账号密码告诉我们当然可以, 但是这样有如下隐患:
- 我们拿到了用户的密码, 这样很不安全. 而且任意一个应用被黑, 所有相关站点均受影响
- QQ 需要支持密码登录, 但是单纯密码登录并不安全. 因此进而影响 QQ 的安全问题
- 我们拿到密码之后, 就相当于拥有了用户的所有信息, 这样无法进行权限限制
- 可能只是希望授权用户名和头像, 但是连着好友列表一起交代出去了
- 用户一旦修改密码, 所有之前授权过的应用全部失效
基于以上原因, 就需要有这样一套机制:
- 不基于密码的授权
- 授权存在时间限制, 并且随时可以收回权限
- 收回单个应用的权限, 不会对其他已授权的应用造成任何影响
- 可以仅授予个别权限, 而不是所有
没错, OAuth
就是为了解决这个问题而提出来的.
介绍
OAuth
是什么呢? 在RFC 文档中是这样介绍的.
OAuth provides a method for clients to access server resources on behalf of a resource owner (such as a different client or an end- user). It also provides a process for end-users to authorize third- party access to their server resources without sharing their credentials (typically, a username and password pair), using user- agent redirections.
写的比较官方哈, 简单说, 就是授权他人以拥有临时访问资源的权限.
那么, OAuth1.0
是如何实现这样的机制呢?
实现
在介绍其实现之前, 需要先来了解OAuth
中的几个概念.
- 角色
- Consumer 消费者, 既需要访问资源的应用
- ServiceProvider 服务提供者, 既提供资源的服务器
- User 用户
依旧使用我们上面的例子, 我们的博客网站需要接入 QQ 登录. 在这里, 博客网站就是其中的Consumer, 而 QQ 服务器就是ServiceProvider了. 为了方便立即, 下面均称为 博客网站 QQ服务器. 我们获取授权的流程大致如下图:
对其中的各个流程进行介绍
流程
在请求之前, 博客网站需要到 QQ 服务器 进行注册, 并获得如下标识:
- consumer_key: 网站标识符
- consumer_secret: 网站使用的私钥
1. 获取 request_token
网站向QQ 服务器申请一个临时凭据, 用来在本次授权过程中进行校验.
携带参数
- oauth_consumer_key
- oauth_signature_method: 签名使用的生成方法
- oauth_signature: 本次请求的签名, 使用consumer_secret生成
- oauth_timestamp 时间戳, 用于对本次请求进行校验
- oauth_callback 回调链接, 用于在用户授权之后回调通知消费者
- oauth_nonce 随机字符串
- 此参数用来防止重放攻击, 即别人拿到请求链接再次请求
- 因此, 服务提供者会对其进行验证是否处理过
返回数据
- request_token: 此 token 仅用作后面授权中校验使用, 以及对本次授权进行标识
- request_secret: 用于本次授权的后续请求进行加密, 使用在第5步
2. 重定向到服务器授权页面
将页面重定向到 QQ 服务器 的授权页面.
携带参数
- request_token: 用来向 QQ 服务器标识本次授权
3. 用户在 QQ 服务器完成授权操作
用户在 QQ 服务器的授权页面进行登录并授权
4. 回调函数通知授权成功
用户授权成功后, QQ 服务器将链接重定向到 第一步指定的 回调链接. 并在回调链接上附带结果:
- request_token: 用来对本次授权进行标识. 毕竟博客网站收到回调时, 需要知道是哪个用户授权成功了.
- verifier: 在下一步获取 access_token 中检验使用. 具体作用在下一步说明.
5. 授权成功后获取 access_token + access_secret
此时用户已经同意了权限的授予, 博客网站可以到 QQ 服务器获取用户的授权码了.
返回数据
- access_token
- access_token_secret
用户后续就使用这两个授权信息到 QQ 服务器请求资源.
access_token_secret作用
这里有个小疑问, 既然有了access_token
, 又为什么需要access_token_secret
呢?
因为访问数据使用HTTP
协议, 对数据的传输过程没有安全保障. 在后续访问资源时, 若仅使用consumer_secret
对请求进行签名, 若consumer_secret
泄露了, 那么攻击者只要获得用户的access_token
就拥有了其权限. 而access_token
又需要在访问是携带在参数中对权限进行标识. 故而十分危险.
在生成签名时, 额外加上access_token_secret
进行签名, 而access_token_secret
不会在后续访问中传递, 每个用户又都是不同的, 因此即使consumer_secret
泄露了, 攻击者也无法获得所有用户的权限.
携带参数
- consumer_key
- request_token
- signature_method
- timestamp
- nonce
- verifier
- signature
verifier作用
那么, 这里的oauth_verifier
有必要么? 其实它是为了防止 session固话攻击, 感兴趣的可以搜索"OAuth Session Fixation Attack"查看具体内容. 这里简单介绍一下.
假设, 有一个攻击者监控了你的网络请求, 因为使用了HTTP
协议, 明文信息也没什么秘密. 那么攻击者就可以在第一步时获得本次授权的request_token
. 同时攻击者又通过暴力破解或其他方法, 获得了consumer_secret
.
此时, 若没有verifier
参数, 本次请求的所有参数攻击者均可以构造. 若攻击者频繁访问本次请求, 又恰好在用户授权完成和网站回调的间隙发起了合法访问, 就会成功获得用户的access_token
.
通过添加随机的verifier
可以使得本次请求不可预测.
问题
对于OAuth1.0
实现的一些问题:
- 使用了
HTTP
协议, 安全性较低 - 使用
HTTP
协议, 没有对服务提供者的真实性进行校验 - 对非web应用(如安卓)支持很不友好. (也有通过 PIN 码实现的, 这里不展开介绍了)
- 签名过程复杂. 需要同时使用
consumer_secret+access_token_secret
进行签名. (也是因为使用HTTP
协议)
这些问题在OAuth2.0
得到了解决, 详情查看下一篇文章: OAuth2.0介绍