freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

OAuth 2.0 认证原理及漏洞(不安全配置)
2022-12-13 22:44:49
所属地 江西省

OAuth 2.0 机制原理

OAuth 2.0是目前最流行的授权机制,用来授权第三方应用,获取用户数据

尽管OAuth 2.0是当前标准,但一些网站仍在使用旧版本1aOAuth 2.0是从头开始编写的,而不是直接从OAuth 1.0开发的。结果,两者非常不同。目前,术语 “OAuth” 专指OAuth 2.0

OAuth就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用

1670942384_63988eb091f7d290e1675.png!small

( 如图便是一种 OAuth 授权登录 )

简单来说:第三方登录,实质就是OAuth授权。

用户想要登录A网站,A网站让用户提供第三方网站的数据,证明自己的身份。获取第三方网站的身份数据,就需要OAuth授权

OAuth身份验证的结果大致类似于基于SAML的单点登录 (SSO)

比如说用户登录网站A需要进行身份验证,且允许使用Google账户登录:

  • A网站让用户跳转到Google
  • Google要求用户登录,用户登录Google后,Google询问用户:A网站要求获取xxx权限/xxx信息,是否同意?
  • 用户同意,Google就会重定向回A网站,并发回一个授权码
  • A网站使用授权码向Google请求令牌
  • Google返回令牌给A
  • A网站使用令牌,向Google请求用户身份

如果说的官方一点,就是:

OAuth在 "第三方应用" 与 "服务提供商" 之间,设置了一个授权层

"第三方应用" 不能直接登录 "服务提供商" ,只能登录授权层,以此将用户与客户端区分开来

"第三方应用"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。"第三方应用"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"第三方应用"开放用户储存的资料 当用户登录了第三方应用后,会跳转到服务提供商获取一次性用户授权凭据),再跳转回来交给第三方应用,第三方应用的服务器会把授权凭据和服务提供商给它的身份凭据一起交给服务方,这样服务方既可以确定第三方应用得到了用户服务授权,又可以确定第三方应用的身份是可以信任的,最终第三方应用可以顺利的获取到服务商提供的web API的接口数据

应用场景

现在各大开放平台,如微信开放平台、腾讯开放平台、百度开放平台等大部分的开放平台都是使用的OAuth 2.0协议作为支撑

  1. 客户端App使用三方登录;
  2. 微信小程序登录授权;
  3. 多个服务的统一登录认证中心、内部系统之间受保护资源请求

令牌(token)

令牌在OAuth中验证身份的作用上与使用密码是一致的,但其有以下特点:

  • 令牌生命周期较短,到期遍会失效
  • 令牌可被数据所有者撤销,且之后基本不会产生相同的令牌
  • 令牌对权限的限制更为严格更加细化,比如说:读令牌、写令牌等

以上的有点让OAuth在身份验证的过程中使用了令牌而不是密码,以上也可以说是OAuth的优点

但是虽然具备以上优点,令牌依旧必须保密,令牌泄露的后果和密码泄露一样严重

获得令牌的方式

综上所述:OAuth的核心就是通过设置一个授权层,向第三方应用颁发令牌

OAuth 2.0规定了四种获得令牌的方式:

  • 授权码(authorization-code
  • 隐藏式(implicit)— 危险
  • 密码式(password
  • 客户端凭证(client credentials

其中有两个概念容易弄混:

  • 授权码:请求令牌
  • 令牌:到用户进行了认证的地方请求用户的数据

授权码authorization-code

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌

也就是最开始举例子所采用的方式,该方式是最常用的流程,也是最安全的

该方式适用于有后端的WEB应用,这样授权码通过前端传送,令牌则存储在后端,所有与资源服务器的通信都在后端完成,前后端分离,避免令牌被泄露

接下来用Burp靶场中的链接(做了一些改动)为例进行举例(后面会完整打该靶场)

  • 第一步:在登陆时,要登陆的网站A会提供一个链接,点击后便会被重定向到B网站,进行身份认证并决定是否要将数据给A网站使用。

    下面是A网站跳转B网站的示意链接

    https://oauth-xxxxxx.web-security-academy.net/auth?

    client_id=xzx6wagk39j6dni18kdl0&

    redirect_uri=https://xxxxxx.web-security-academy.net/oauthcallback&

    response_type=code&

    nonce=879645905&

    scope=openid profile email

    其中参数的含义为:

    • response_type表示要求返回的类型

      code表示要求返回授权码

    • client_idB直到是谁向其发送的请求,用于标识请求的发送方

      xzx6wagk39j6dni18kdl0

    • redirect_uri标识B网站接受或拒绝请求后跳转的网站

      https://xxxxxx.web-security-academy.net/oauth-callback

    • scope表示要求的授权范围

      openid profile emailGoogle 平台默认的scope就是这三个

      USER_OPENID("openid", "Associate you with your personal info on Google", true),
      USER_EMAIL("email", "View your email address", true),
      USER_PROFILE("profile", "View your basic profile info", true),
      
  • 第二步:用户跳转后,B网站会要求用户登录,用户登陆后,B会询问是否同意给予A网站授权,用户同意后,B网站就会跳到redirect_uri参数指定的网址。跳转时,会传回一个授权码

    https://xxxxxx.web-security-academy.net/oauth-callback?code=AUTHORIZATION_CODE

  • 第三步:A网站拿到B返回的授权码后,便可以在后端通过授权码向B网站请求令牌了

    [https://oauth-authorization-server.com/token?

    client_id=12345&

    client_secret=SECRET&

    redirect_uri=https://clientapp.com/callback&

    grant_type=authorization_code&

    code=a1b2c3d4e5f6g7h8&

    scope=openid email profile]

    client_idclient_secret用于让B确认A的身份,而且client_secret是保密的,所以为保证安全,该请求一般只在后端发送

    • grant_type=authorization_code

      表示使用的授权方式为授权码

    • code=a1b2c3d4e5f6g7h8

      上一步拿到的授权码

    • https://client-app.com/callback

      令牌颁发后的回调网址

  • 第四步:B网站收到请求以后,就会颁发令牌

    具体做法是向redirect_uri指定的网址,发送一段JSON数据

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

    其中access_token字段就是令牌

隐藏式implicit

不安全

有些Web应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端(当然不排除有错误配置为该模式的情况)

RFC 6749就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit

  • 第一步:A 网站提供一个链接,要求用户跳转到B网站进行登录,并将B网站的用户数据授权给A网站使用

    /authorization?

    client_id=12345&

    redirect_uri=https://clientapp.com/callback&

    response_type=token&

    scope=openid profile&

    state=ae13d489bd00e3c24

    其中重点关注的参数为:

    • response_type=token代表要求直接返回令牌
  • 第二步:用户跳转到B网站进行身份的认证,登录后若用户同意授予A网站访问用户数据的权力,则B网站就会跳转到redirect_uri所指定的网址,并且把生成的令牌作为URL参数,传递给A网站

    https://clientapp.com/callback#token=ACCESS_TOKEN其中ACCESS_TOKEN部分就是返回的令牌

    此处返回令牌时并没有以查询字符串的形式返回,而是以锚点(fragment)的形式返回,主要受历史原因影响,由于OAuth2.0允许跳转的网站是HTTP协议,存在攻击的可能,而浏览器跳转时,锚点不会被发到服务器,减少令牌在传输过程中泄露风险

    [但注意,这种将令牌直接传给前端的方式依旧是非常危险的,避免使用该方式若必须使用,要保证对令牌有效期控制的很短)]


锚点#

#代表了网页中的一个位置,其右面的字符串就是该位置的标识符,比如在支持锚点的网页中,例如输入以下URL

https://www.shtwo.top/pwn1#canary访问时就会将canary位置滚动到可视区域

锚点的特性

  • HTTP请求不包括#:锚点的作用在于告诉浏览器定位到哪里,与服务端没有关系,所以发出HTTP请求时不会携带锚点以及后面的字符串

    也就是说https://www.shtwo.top/?color=#fff实际上发出HTTP请求时报文中请求的URI实际是?color=

    这也就解释了为什么上述令牌要放在#后面返回

    这种过滤方式是在浏览器层面进行的过滤,所以若将#URL 编码为%23就可以成功发送:

    https://www.shtwo.top/?color=%23fff

  • 默认情况下,搜索引擎会忽略URL#的部分

  • 由于搜索引擎会忽略#后面的部分,所以若是希望Ajax生成的内容被浏览引擎读取,会有麻烦,此时可以使用#!来代替,Google会自动将#!转成查询字符串__escaped_fragment__的值


密码式password

非常不安全

RFC 6749也允许用户直接提交用户名和密码,待登录网站直接拿着用户名和密码到认证网站认证身份,申请令牌

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

    /token?

    client_id=12345&

    grant_type=password&

    username=^USERNAME^&

    password=^PASSWORD^&

  • 第二步B网站验证身份通过后,直接给出令牌

    此时不需要跳转,直接把令牌以JSON的形式通过HTTP回应

毫无疑问,非常不安全

凭证式client credentials

该方式一般不用来请求令牌,多用于多用户共享同一令牌时进行令牌的换

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

    /token?grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

  • 第二步:B验证通过以后,直接返回令牌

使用令牌

A网站拿到令牌以后,就可以向B网站的API请求数据了。

此时,每个发到BAPI的请求用户信息的请求,都必须通过在HTTP头部添加Authorization字段带上令牌

具体格式为:

Authorization: Bearer ACCESS_TOKEN

1670942480_63988f10392e6aab05f3e.png!small?1670942466938

更新令牌

B网站颁发令牌的时候,一般会一次性颁发两个令牌:

  • 获取数据access_token
  • 刷新获取新的令牌refresh_token

在申请的access_token令牌到期前,用户使用refresh token发出刷新令牌请求,即可更新令牌

/token?grant_type=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&refresh_token=REFRESH_TOKEN

OAuth 2.0 存在的问题

了解了 OAuth 2.0的原理后,可以发现其可能存在的问题/漏洞,大多数源于其缺乏内置的安全机制,与开发者/使用者是否采取了正确的获取令牌的方式息息相关,那么接下来就通过Burp官方提供的靶场来说几种存在安全风险的情况

至于如何判断当前是一个OAuth2.0的认证,方法很多,而且其特征很明显,相信大家通过前面的介绍已经可以区分,此处就不过多介绍了

风险点 1 :使用不合适的方式获取令牌

存在风险的令牌获取方式:

  • 隐藏式implicit
  • 密码式password

且不对传输的access_tokenusername/password加密的话,就会造成中间人攻击

靶场信息:

1670942487_63988f1758000101579c0.png!small?1670942474130

目标:

客户端应用程序的错误验证使攻击者有可能在不知道密码的情况下登录其他用户的帐户。

要解决实验室问题,请登录Carlos的帐户。他的电子邮件地址是carlos@carlos-montoya.net

靶机地址:Lab: Authentication bypass via OAuth implicit flow | Web Security Academy

基本信息:进入靶场后,到登陆界面登陆时发现进行的是基于 OAuth 2.0的身份认证方式

1670942493_63988f1d8e755611ed7b8.png!small?1670942481790

于是打开Burp进行抓包

1670942499_63988f23424c0c2c9b089.png!small?1670942486134

通过其请求包中的参数response_type=token发现采用的获取令牌的方式为:隐藏式

采用该方式说明access_token令牌会被传回也就意味着可以被获取到,但前提是返回的带有令牌的JSON信息不被加密

果然其返回的令牌token为:

1670942503_63988f2750c91ec82ddb9.png!small?1670942489910

于是可以看到当前网站向提供认证的网站发出了请求的报文中带上了这个token

1670942508_63988f2c31ea72f0623b6.png!small?1670942494648

有了这串token,我们就可以找到刚刚使用该token的身份认证的报文中,修改其email达到绕过的目的

1670942513_63988f31713c6b8032370.png!small?1670942501243

将该报文发到Repeater进行重放,并替换其email字段

1670942517_63988f35d5628617508d7.png!small?1670942504609

此时得到了一个用于Set-Cookie的新session并进行302的重定向,将其生成对应的URL

1670942522_63988f3acbeac9b258123.png!small?1670942509486

复制到浏览器请求

1670942526_63988f3ebfb7879dff0fd.png!small?1670942513502

大功告成

风险点 2 :缺乏随机 token 值

此处的token值得是用于预防CSRF所使用的token,并不是前面所说的access_token,由于当当前网站成功获得了用来访问存在着认证完用户信息的网站时(获取了access_token),需要使用access_token这个令牌确认当前的身份,那么这个access_token的作用就很像CSRF中经常尝试获取并利用的session cookie一样,可以代表当前用户的身份,所以若不加以限制,势必会造成 CSRF 攻击

而对于OAuth中,常用state参数来传递一个随机的用户防止CSRFtoken

/authorization?

client_id=12345&

redirect_uri=https://clientapp.com/callback&

response_type=token&

scope=openid profile&

state=ae13d489bd00e3c24

拿最开始的例子来说,可以明显看到其携带了state参数,且是一个随机数

所以若是进行了错误的配置,没有启用state参数,就有可能造成CSRF攻击

靶机见:

Lab: Forced OAuth profile linking | Web Security Academy

风险点 3:不完善的redirect_uri限制

通过前面的学习,我们知道在身份服务器验证用户身份(成功或失败)后,需要302跳转到redirect_uri处,那么对于重定位的地址,若不进行限制就可以跳转到任意构造好的URL,通过恶意构造iframe标签,引导用户点击发起访问,这也可导致最终的令牌被发送到攻击者构造的 URL 处被攻击者获取

所以,客户端应用程序的最佳做法是在向OAuth服务注册时提供其真实回调URI的白名单

靶机见:

Lab: OAuth account hijacking via redirect_uri | Web Security Academy

参考文章/深入学习

自定义Scope

OAuth 2.0 authentication vulnerabilities | Web Security Academy

OAuth2.0原理浅析

00 开篇词 为什么要学OAuth 2.0?.md

OAuth2.0认证和授权机制讲解

# 网络安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录