OAuth2.0安全设计之Authorization Code

OAuth 2.0 有 4 种认证流程:javascript

  • 受权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

下面以微信为例介绍最多见的也是最安全的 Authorization Code认证流程。php

1、受权流程说明

       微信OAuth2.0受权登陆让微信用户使用微信身份安全登陆第三方应用或网站,在微信用户受权登陆已接入微信OAuth2.0的第三方应用后,第三方能够获取到用户的接口调用凭证(access_token),
经过access_token能够进行微信开放平台受权关系接口调用,从而可实现获取微信用户基本开放信息和帮助用户实现基础开放功能等。
  微信OAuth2.0受权登陆目前支持authorization_code模式,适用于拥有server端的应用受权。该模式总体流程为:html

       获取access_token时序图:前端

    
 
 

2、具体实现过程

下面具体介绍一下微信对这个协议的具体实现过程。java

第1步:开发者在微信开放平台申请接入并成功获取到appid和AppSecret,并配置回调域名。web

第2步:构造微信登陆二维码的超连接以下:json

参数说明
参数
是否必须
说明

appidapi

应用惟一标识(前面认证网页应用中得到)

redirect_uri跨域

重定向地址,须要进行UrlEncode(前面认证网页应用中得到)

response_type数组

填code

scope

应用受权做用域,拥有多个做用域用逗号(,)分隔,网页应用目前仅填写snsapi_login便可

state

用于保持请求和回调的状态,受权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验

 返回说明

 用户容许受权后,将会重定向到redirect_uri的网址上,而且带上code和state参数

redirect_uri?code=CODE&state=STATE

若用户禁止受权,则重定向后不会带上code参数,仅会带上state参数

redirect_uri?state=STATE

实际抓包示例:

https://open.weixin.qq.com/connect/qrconnect?response_type=code&appid=wx2198c66352420194&redirect_uri=https%3A%2F%2Fpassform.test.com%2Fv3%2Fweb%2Flogin%2FwechatCallBack%3FisPc%3D1%26randomNum%3D%26redirect_uri%3Dhttps%253A%252F%252Fwww.test.com%252F%253Fopenid%253D33336839a7398ce8%26client_id%3D30&scope=snsapi_login&state=1614336736067

其中appid参数为开发者在第一步中申请到的appid, scope参数为受权应用的权限列表,redirect_uri为受权成功后的回调地址。

 
 

第3步:假如用户赞成受权,在微信登陆成功后会跳转到redirect_uri参数指定的URL,并在URL尾部追加code参数(即Authorization Code),如上述示例则会跳转到:

https://passform.test.com/v3/web/login/wechatCallBack?isPc=1&randomNum=&redirect_uri=https%3A%2F%2Fwww.test.com%2F%3Fopenid%3D33336839a7398ce8&client_id=30&code=053isZFa12PMAA0NVeGa1yR9300isZFe&state=1614336736067

而后,咱们能够经过Authorization Code去获取用户openid和access_token,进而得到用户的信息。
 
 

第4步:经过Authorization Code获取Access Token和openid,服务器端构造以下请求便可获取Access Token和openid:

参数解释以下:

grant_type

受权类型,此值固定为“authorization_code”。

client_id

申请微信登陆成功后,分配给网站的appid。

client_secret

申请微信登陆成功后,分配给网站的appkey。

code

上一步返回的Authorization Code。

redirect_uri

与上面一步中传入的redirect_uri保持一致。

返回说明
正确的返回:
{
     "access_token":"ACCESS_TOKEN",
     "expires_in":7200,
     "refresh_token":"REFRESH_TOKEN",
     "openid":"OPENID",
     "scope":"SCOPE",
     "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
参数说明:

参数

说明

access_token

接口调用凭证

expires_in

access_token接口调用凭证超时时间,单位(秒)

refresh_token

用户刷新access_token

openid

受权用户惟一标识

scope

用户受权的做用域,使用逗号(,)分隔

unionid

当且仅当该网站应用已得到该用户的userinfo受权时,才会出现该字段。

错误返回样例:
{"errcode":40029,"errmsg":"invalid code"}
 
 

第5步:使用Access Token以及OpenID来访问用户数据

构造以下请求便可访问用户数据:
参数说明

参数

是否必须

说明

access_token

调用凭证(上一个请求中得到)

openid

普通用户的标识,对当前开发者账号惟一(上一个请求中得到)

lang

国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语,默认为zh-CN

返回说明
正确的Json返回结果:
{
     "openid":"OPENID",
     "nickname":"NICKNAME",
     "sex":1,
     "province":"PROVINCE",
     "city":"CITY",
     "country":"COUNTRY",
     "privilege":[
         "PRIVILEGE1",
         "PRIVILEGE2"
     ],
     "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
 
 

参数

说明

openid

普通用户的标识,对当前开发者账号惟一

nickname

普通用户昵称

sex

普通用户性别,1为男性,2为女性

province

普通用户我的资料填写的省份

city

普通用户我的资料填写的城市

country

国家,如中国为CN

headimgurl

用户头像,最后一个数值表明正方形头像大小(有0、4六、6四、9六、132数值可选,0表明640*640正方形头像),用户没有头像时该项为空

privilege

用户特权信息,json数组,如微信沃卡用户为(chinaunicom)

unionid

用户统一标识。针对一个微信开放平台账号下的应用,同一用户的unionid是惟一的。

错误的Json返回示例:
{ 
     "errcode":40003,"errmsg":"invalid openid"
}

3、常见不安全设计形成的风险

风险1:redirect_uri回调域名欺骗

(1)未验证redirect_uri是否与注册的回调地址匹配

在上述实现的第二步中将redirect_uri修改成攻击者控制站点,用户在受权登陆后将携带Authorization Code跳转到攻击者控制站点,攻击者从URL参数中便可得到Authorization Code并实现用户劫持。服务端必须验证client_id(APPID)和redirect_uri规定的域一致,若是不一致则没法登录
其实腾讯在实现第三方登陆接入的时候早就考虑过这种老套的攻击方式,因而,开发者在集成微信登陆时必须在微信开放平台上填写网站的回调地址,在进行登陆验证的时候若是redirect_uri中的值与设置好的回调地址不一样则会拒绝访问:

 
 

这样就防止了攻击者篡改redirect_uri为恶意站点的钓鱼攻击。

可是如今又提出了一种看似合理的绕过方法:

利用合法网站的URL重定向漏洞绕过redirect_uri中的域名白名单限制。

假设我有一个合法的网站whitehat.com,攻击者控制一个恶意站点hacker.com

攻击者能够构造这样一个连接来绕过redirect_uri中的域名白名单限制:

http://whitehat.com/index.php?Redirect=http%3a%2f%2fhacker.com%2findex.php

其中Redirect参数指定的为重定向地址

这样的话,把这个URL地址传给redirect_uri便可构造一个恶意连接,实现用户受权微信登陆后跳转到hacker.com

可是用户的受权令牌Authorization Code真的会被传送到hacker.com吗?

咱们把上述URL传给redirect_uri,跳转到的URL地址以下:

http://whitehat.com/index.php?Redirect=http%3a%2f%2fhacker.com%2findex.php?code=****

细心的人已经发现了,这个连接仍是跳转到http://hacker.com/index.php而不是http://hacker.com/index.php?code=****

这是由于code参数前面的&符号没有URL编码,所以code参数被whitehat.com处理而不是属于Redirect参数的一部分。

所以,第一种攻击模型只能用来构造登陆后的钓鱼攻击,一般状况下Authorization Code不会被传送到攻击者控制的站点中

(2)未设置Authorization Code使用一次就失效

将第二步实现的redirect_uri改成能够引入外链的合法URL地址,这样当合法用户登陆后加载此页面的外链时,攻击者就能够从其控制的服务器中在referer消息头中得到泄露的Authorization Code。听说这种攻击方法横扫国内各大站点,这个攻击方法在此RFC文档的Security Considerations中已经提到过:

同时也给出了相应的安全建议:

即Authorization Code在获取后必须在短期内失效并且只能被使用一次。这种方法在理论上确实能够有效的阻止上述的攻击方式,情景分析以下:

(1)攻击者构造恶意连接发送给用户,其中redirect_uri=http://bbs.test.com/index.php
(2)用户点击连接登陆后,回调地址为:http://bbs.test.com/index.php?code=****
(3)用户携带code向服务器发送请求加载此页面,加载的页面中含有攻击者放置的外链(例如头像中的图片连接等),用户加载外链中的图片,攻击者从referer消息头中得到用户的code

因为Authorization Code是经过redirect_uri浏览器回调传输,容易被截取,服务器生成的临时Authorization Code必须是一次有效,客户端使用一次后当即失效而且有效期很短,通常推荐30s有效期,能够保证临时Authorization Code被客户端正常消费后不会被再次使用

风险2:redirect_url XSS跨域攻击

好比构造一个认证请求,redirect_uri = http://app.com/test?callback=<script src="http://app2.com?getToken.php"></script>

服务器端须要对redirect_uri进行检查禁止特殊字符输入,而且对redirect_uri进行全匹配,不作模糊匹配能够杜绝XSS攻击。

风险3:未添加State 防止CSRF

第2步认证请求url中state参数是最容易被忽略的,大部分IDP不会强制要求客户端使用state参数。与 CSRF 攻击相似,若是 state 参数为空,做为攻击者,

1. 先申请一个新的,专门用于攻击他人的帐号;
2. 而后走正常流程,跳到微信上去登陆此帐号;
3. 登陆成功以后,微信带着 code 回跳到第三方站点,如www.test.com,这个时候,攻击者拦截本身的请求让他再也不往下进行,而直接将带 code 的连接发给受害者,并欺骗受害者点击;
4. 受害人点击连接以后,继续攻击者帐号的登陆流程,不知不觉登陆了攻击者的帐号

受害者若是这个时候没察觉此帐号不是他本人的,传了一些隐私文件,如照片啥的,攻击者立马就能经过本身的帐号看到。

而 state 参数若是利用起来,看成 CSRF Token,就能避免此事的发生:

1. 攻击者依旧获取 code 并打算骗受害者点击
2. 受害者点击连接,但因服务器(好比 www.test.com)分配给受害者的设备的 state 值和连接里面的(分配给攻击者的)state 值不同,服务器(test.com)直接返回验证 state 失败。

因此安全的实现是:

客户端每次请求生成惟一字符串在请求中放到state参数中,服务端认证成功返回Authorization Code会带上state参数,客户端验证state是不是本身生成的惟一串,能够肯定此次请求是有客户端真实发出的,不是黑客伪造的请求

风险4:Access_Token泄露

  • 因为Access_Token是经过http协议从服务器端传输给客户端,为了防止旁路监听泄露Access_Token,服务器必须提供https来保证传输通道的安全性(TSL的要求)
  • 客户端获取Access_Token,应该在后台与服务端交互获取Access_Token,不容许Access_Token传给前端直接使用
  • 须要保证Access_Token信息的不可猜想性,以防止被猜想获得

风险5:令牌有效性漏洞

  • 维持refresh_token和第三方应用的绑定,刷新失效机制的设计不容许长期有效的token存在;

4、加强OAuth2.0协议设计及使用规范

OAuth2.0协议安全性进行进一步加强。

  • 对颁发出去的token权限进行限制,不一样用户申请的token根据人员所属组织、角色、岗位进行数据隔离
  • 对登陆过程安全性加强,对登陆验证方式进行丰富,支持静态密码、手机验证码、OTP、生物识别、FIDO
  • 对Token颁发后的生命周期管理,能够按策略主动注销颁发的Token
  • 对使用OAuth过程进行行为分析,对登陆过程进行风险识别
  • 按照不一样应用的安全等级进行分级,不一样安全级别应用实现二次认证,保障关键系统的安全访问
 
 

参考资料:

https://cloud.tencent.com/developer/article/1447723
https://www.anquanke.com/post/id/98392
https://www.freebuf.com/articles/web/252254.html
https://xz.aliyun.com/t/2260
https://wooyun.js.org/drops/OAuth%202.0%E5%AE%89%E5%85%A8%E6%A1%88%E4%BE%8B%E5%9B%9E%E9%A1%BE.html
相关文章
相关标签/搜索