OAuth 2.0 有 4 种认证流程:

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

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

一、授权流程说明

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

获取access_token时序图:

    

二、具体实现过程

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

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

第2步:构造微信登录二维码的超链接如下:

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

appid

应用唯一标识(前面认证网页应用中获得)

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%253D14996839a7398ce8%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%3D14996839a7398ce8&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、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空

privilege

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

unionid

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

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

三、常见不安全设计造成的风险

风险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存在;

四、增强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

OAuth2.0安全设计之Authorization Code的更多相关文章

  1. The OAuth 2.0 Authorization Framework OAuth2.0的核心角色code 扫码登录

    RFC 6749 - The OAuth 2.0 Authorization Framework https://tools.ietf.org/html/rfc6749 The OAuth 2.0 a ...

  2. IdentityServer4:IdentityServer4+API+Client实践OAuth2.0客户端模式(1)

    一.OAuth2.0 1.OAuth2.0概念 OAuth2.0(Open Authorization)是一个开放授权协议:第三方应用不需要接触到用户的账户信息(如用户名密码),通过用户的授权访问用户 ...

  3. Force.com微信开发系列(七)OAuth2.0网页授权

    OAuth是一个开放协议,允许用户让第三方应用以安全且标准的方式获取该用户在某一网站上存储的私密资源(如用户个人信息.照片.视频.联系人列表),而无须将用户名和密码提供给第三方应用.本文将详细介绍OA ...

  4. Java微信公众平台开发(十六)--微信网页授权(OAuth2.0授权)获取用户基本信息

    转自:http://www.cuiyongzhi.com/post/78.html 好长时间没有写文章了,主要是最近的工作和生活上的事情比较多而且繁琐,其实到现在我依然还是感觉有些迷茫,最后还是决定静 ...

  5. OAuth2.0 授权许可 之 Authorization Code

    写在前面: 在前一篇博客<OAuth2.0 原理简介>中我们已经了解了OAuth2.0的原理以及它是如何工作的,那么本篇我们将来聊一聊OAuth的一种授权许可方式:授权码(Authoriz ...

  6. Apache Oltu 实现 OAuth2.0 服务端【授权码模式(Authorization Code)】

    要实现OAuth服务端,就得先理解客户端的调用流程,服务提供商实现可能也有些区别,实现OAuth服务端的方式很多,具体可能看 http://oauth.net/code/ 各语言的实现有(我使用了Ap ...

  7. Oauth2.0(五):Authorization Code 授权

    Authorization Code 方式适用于有自己的服务器的应用.之所以叫这个名字,是因为流程中引入了一个叫做 authorization code 的东西.这个东西是一个一次性的临时凭证,用来换 ...

  8. OAuth2.0和企业内部统一登录,token验证方式,OAuth2.0的 Authorization code grant 和 Implicit grant区别

    统一登录是个很多应用系统都要考虑的问题,多个项目的话最好前期进行统一设计,否则后面改造兼容很麻烦: cas认证的方式:新公司都是老项目,用的是cas认证的方式,比较重而且依赖较多,winform的项目 ...

  9. 关于OAuth2.0 Authorization Code + PKCE flow在原生客户端(Native App)下集成的一点思考

    写在前面 前几天看了园友的一篇文章被广泛使用的OAuth2.0的密码模式已经废了,放弃吧 被再次提起: Implicit Flow Password Grant,均已被标记为Legacy,且OAuth ...

随机推荐

  1. 在线工具生成接入信息mqtt.fx快速接入阿里云

    在线工具生成接入信息mqtt.fx快速接入阿里云 在使用阿里云获取的三元组信息进行接入的时候,往往需要加密生成接入信息之后才能进行接入,因此我根据阿里云提供的加密工具实现了一个阿里云物联网平台mqtt ...

  2. 2019 ccpc秦皇岛

    1006 (dfs) #include <bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const dou ...

  3. AtCoder Beginner Contest 162

    比赛链接:https://atcoder.jp/contests/abc162/tasks A - Lucky 7 #include <bits/stdc++.h> using names ...

  4. Codeforces Round #345 (Div. 1) C. Table Compression (并查集)

    Little Petya is now fond of data compression algorithms. He has already studied gz, bz, zip algorith ...

  5. Dapr微服务应用开发系列0:概述

    题记:Dapr是什么,Dapr包含什么,为什么要用Dapr. Dapr是什么 Dapr(Distributed Application Runtime),是微软Azure内部创新孵化团队的一个开源项目 ...

  6. CF1462-C. Unique Number

    题意: 给出一个数字x,让你找出一个由1到9这九个数字组成的数字,这个数字的每一位加起来等于x,并且1到9每个数字只能出现一次.若能找到这样的数字,输出这其中最小的一个,否则输出-1. 思路: 利用二 ...

  7. 局部变量 static new 结构体指针

    struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(NULL) {} }; 有一个函数利用LisNode ...

  8. 关于Matlab GUI的一些经验总结

    注:此文来自转载,侵删去年做了一些关于Matlab GUI的程序,现在又要做相关的东西,回想起来,当时很多经验没有记录下来,现在回顾起来始终觉得不爽,所以从现在开始,一定要勤写记录. 从简单的例子说起 ...

  9. CVE-2019-13272:Linux本地内核提权漏洞复现

    0x00 简介 2019年07月20日,Linux正式修复了一个本地内核提权漏洞.通过此漏洞,攻击者可将普通权限用户提升为Root权限. 0x01 漏洞概述 当调用PTRACE_TRACEME时,pt ...

  10. js uppercase first letter

    js uppercase first letter const str = `abc`; str.slice(0, 1).toUpperCase(); // "A" str.sli ...