OAuth2.0下被忽略的state

Posted by

引言

一直以来,大家都熟悉Oauth2.0相关处理流程,在日常使用中也经常遇见,比如网站使用微信授权认证登录,或者用微博的授权登录等,相信对于对接过微信网页授权的童鞋这个也再熟悉不过。

之前公司的产品登录认证,也是采用了该模式,而目前却发现有人发现了登录系统下的漏洞,利用该漏洞违规登录。

一、登录认证

1.1、OAuth2.0的四种授权模式

日常使用过程中,我们接触到的最多的即是授权码模式。Oauth2.0一共提出了4种授权方式,分别是以下四种:

  1. 授权码模式(即通过code换取令牌)
  2. Implicit(绕过code直接颁发令牌)
  3. 账号密码模式(向认证中心提交账号密码)
  4. 凭证模式(应用端用认证中心约定的固定凭证获得授权)

考虑到安全性,最高的即为授权码模式,避免了向第三方暴露令牌。

而之前公司的产品登录有部分业务直接使用的access_token,实际上采用了第二种方式。所以从根本上来说,已经存在了一定的安全性问题。

同时有部分产品,接入的是授权码模式,流程如下。

1.2、auth code登录认证流程

PlantUML Syntax:

@startuml
'https://plantuml.com/sequence-diagram

autonumber

Actor user
participant client
participant app
participant auth

user -> client: Login app
activate client
client -> auth: GET https://auth.com/login?app_id=app
activate auth
auth --> client: 200 [Content of Login Page]
deactivate auth
client --> user: Display Page
activate user

user -> client: Submit form
deactivate user
client -> auth: POST https://auth.com/login
activate auth
auth -> auth: Authenticate user
auth --> client: Set-cookie: session_id=*** \n302 Location:https://app.com/login?code=***

client -> app: GET Location:https://app.cn/login?code=***
activate app
app -> auth: GET https://auth.com/api/token?code=***
auth --> app: 200 [Json Content With access_token]
app -> auth: GET https://auth.com/api/auth?access_token=***
auth --> app: 200 [Json Content]
deactivate auth
app --> client: Set-cookie: app_session_id=*** \n302 Location:https://app.com/pages
client -> app: GET https://app.com/pages
app --> client: 200 [Content of Page]
deactivate app
client --> user: Display Page

@enduml

1.3、直接颁发凭证

相比上述的1.2的流程,少了code换取access_token的流程,在有认证服务端登录后,直接重定向到产品的链接上带上凭证access_token

PlantUML Syntax:

@startuml
'https://plantuml.com/sequence-diagram

autonumber

Actor user
participant client
participant app
participant auth

user -> client: Login app
activate client
client -> auth: GET https://auth.com/login?app_id=app
activate auth
auth --> client: 200 [Content of Login Page]
deactivate auth
client --> user: Display Page
activate user

user -> client: Submit form
deactivate user
client -> auth: POST https://auth.com/login
activate auth
auth -> auth: Authenticate user
auth --> client: Set-cookie: session_id=*** \n302 Location:https://app.com/login?access_token=***

client -> app: GET Location:https://app.cn/login?access_token=***
activate app
app -> auth: GET https://auth.com/api/auth?access_token=***
auth --> app: 200 [Json Content]
deactivate auth
app --> client: Set-cookie: app_session_id=*** \n302 Location:https://app.com/pages
client -> app: GET https://app.com/pages
app --> client: 200 [Content of Page]
deactivate app
client --> user: Display Page

@enduml

二、漏洞的产生

2.1、漏洞产生

在1.2和1.3的各自流程中,第8步,用户在认证服务端认证成功之后重定向到业务的链接中,如果此时被用户拦截获取到该链接,然后颁发给其他任何人,对方都可以直接登录该产品。即使是先颁发code,然后换取access_token的流程,依旧可以直接登录。

2.2、漏洞利用

当前发现有用户将自己的账号租用给他人使用,而为了避免直接将自己的账号密码信息提供给他人,于是设计出来一个定制化的登录系统,该系统将在认证服务端账号登录之后重定向的链接进行拦截,然后提供给第三方,这样第三方就可以直接登录进产品。即使第三方可以进到个人中心查看账号,但因为无法更改账号(更新账号信息需要原账号认证),所以不用担心安全问题

2.3、漏洞原因

以上问题暴露的根本问题是,认证服务关于凭证的颁发与校验,其实是不知道申请的客户端到底是谁的。

三、认识state

3.1、疑惑

刚开始碰到上述问题的时候,觉得十分疑惑,我们一直在用的微信网页授权方式,理论上也存在上述漏洞,那怎么可能还一直没有被提出呢?

觉得不太可能,肯定是自身漏掉了某些细节。于是重新翻看相关文档,发现提到了另一个参数:state

3.2、state

以微信网页授权的文档为例,请求获取code的链接示例如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

基础参数含义:

appid:标明当前应用身份

redirect_uri:授权完成回跳地址

response_type:返回类型

scope:授权模式

state:重定向后会带上 state 参数

即state表示应用方请求过去的是什么,最后授权完成的时候,会原样带回来。

3.3、使用state

起初一直在琢磨认证服务端,如何去识别申请凭证的客户端与校验的客户端是否一致,后面发觉其实思路错了。

这里认证客户端是否前后一致,其实是应用方应该处理的。于是就有了state参数。

state由应用端随机产生,并与当前的客户端会话进行绑定。然后请求认证服务时带上该参数。认证服务在授权后,附带上该state参数重定向到应用端地址,此时应用端再判断state是否与当前客户端的会话有过绑定。如果一致则再去进行下一步流程

关于state解释:http://www.rfcreader.com/#rfc6749_line1098

state这里标注出来的必要级别为:RECOMMENDED

同时提到了关于利用state参数防止CSRF攻击:http://www.rfcreader.com/#rfc6749_line2569

四、尝试修复

4.1、认证服务识别客户端

鉴于上述问题,早期思考了一种快速的解决方案,即认证服务端判断客户端IP。

1. 颁发凭证时,认证服务端记录用户IP,与凭证做关联。

2. 校验凭证时,应用端同时传递过来用户IP

3. 认证服务端判断传递过来的IP与原先颁发凭证时记录的IP是否一致,不一致则不通过

问题:该方案在测试的时候发现,如果用户所在的网络环境有多个出口IP,容易造成校验不通过。因此无法实施

4.2、应用端识别客户端

设想:参考state,应用端在申请登录认证时,同时提交state参数,做客户端认证,保证前后申请与认证方一致

五、思考

针对Oauth2.0,之前从未关注过state参数的作用。各种接入文档对于该参数的描述也是optional,也并未提及到该参数使用的具体目的,也就忽略了其作用。

对于此类标准的解决方案,还是得注重文档,明晰作用,解决方案涉及到的着重点,应该更加仔细琢磨

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用*标注