N0rth3ty's Blog.

浅析OAuth 2.0相关安全问题

字数统计: 1.2k阅读时长: 4 min
2019/11/11 Share

OAuth是一套关于授权的网络标准
现在一般使用的是OAuth 2.0

OAuth

OAuth的核心是向第三方应用授予权限,也就是我们常见的第三方登陆

它的优势在于可以方便的控制权限等,不赘述

OAuth一共有四种授权方式

授权码(authorization-code)

授权码也就是我们常用的流程
它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

这里以一次实际登陆A网站的流程为例
首先我们通常的登陆是这样
image
然后我们选择微信登陆
image
然后可以看到是请求了这样的一个数据包,然后同时会生成一个二维码,即让你用微信进行授权
先看一下OAuth相关各个字段的含义

  • response_type=code 表示使用的是授权码方式
  • redirect_uri=http%3A%2F%2Fi.nosec.org% 是微信授权以后的回调地址
  • scope=snsapi_login 代表授权的范围,可能会存在修改scope越权的问题
  • appid是身份校验,标志你来自哪个应用
  • state字段用于防范一种特殊的CSRF,具体后面解释

扫码授权以后会发生跳转,也就是下面这个包
image
code参数极为OAuth中的授权码
我们讲一下这个state参数
state在这里用于防范CSRF,如果没有这个state或者未对state做校验会发生什么呢
看一下利用方式,假设A是被攻击网站,我是攻击者

1
2
3
4
我用自己的微信扫码授权,然后跳转到redirect_uri  
我拦截这个url,https://A.com/xxx?code=081vdwbI1SU6i008y0&state=xxxxxxx
删除state参数后用于钓鱼
受害者点击后会在A网站登陆我的账号

但如果对state参数进行校验的话,受害者就会登陆失败
利用的成果是受害者登陆攻击者指定的账号
这种称为登陆型CSRF吧,一种脑洞大开的攻击方式

A网站拿到授权码以后在后端请求获取令牌进行后续操作即可
后端的交互请求如下

1
2
3
4
5
6
https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL

client_secret是获取令牌的一个校验,其实就是一个秘钥,是你在申请接入OAuth时分配给的,能够避免攻击者取得code后自己去请求获得令牌

隐藏式(implicit)

也就是隐藏了获取授权码的过程,直接获取令牌
通常请求格式如下

1
2
3
4
5
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read

同意授权后B会返回

1
https://a.com/callback#token=ACCESS_TOKEN

注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在”中间人攻击”的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险

(上面这段是引用

解释起来锚点就是一个能快速返回的点,是一个前端的概念,并不会作为参数发送给服务器
但是可以被js取得,然后a通过js取得锚点的令牌就行通信即可

所以该种方式令牌完全在后台进行了传输,避免了中间人攻击
而OAuth设计的目标之一就是要让不支持https的网站使用
所以才会出现授权码这种东西

这里就再谈一下code的设计
为什么code没有防范使用http协议的网站被中间人攻击呢?
因为code拿到以后是要生成令牌的,但是OAuth中code只能使用一次
一个code被使用两次会导致令牌失效,所以即便被中间人窃取
因为受害者肯定会立刻生成令牌,所以并不能产生危害,顶多是授权失败

密码式(password)

密码式就是更进一步的隐藏式,直接通过账号密码进行授权
通常请求格式如下

1
2
3
4
5
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID

密码式不需要跳转,直接返回令牌
该方式风险较大,提一下,不常见

客户端凭证(client credentials)

最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌

简单说这东西是提供给系统本身不是提供给用户使用的

常见请求格式为

1
2
3
4
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET

也是直接返回令牌

参考链接

http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
https://www.chrisyue.com/security-issue-about-oauth-2-0-you-should-know.html

CATALOG
  1. 1. OAuth
    1. 1.1. 授权码(authorization-code)
    2. 1.2. 隐藏式(implicit)
    3. 1.3. 密码式(password)
    4. 1.4. 客户端凭证(client credentials)
    5. 1.5. 参考链接