读者在看这篇文章之前,请先了解 Oauth2.0 的 Authorization Code 授权流程,可以看 Authorization Code 授权原理和实现方法

  我们还是用 A 来代表合作方,用 B 来代表鉴权方。在授权流程中,A 发起授权时,会向 B 传一个 state 参数,而 B 对这个参数只做一个事情,就是在 302 跳转返回的时候把它原封不动的返回。为什么要这么做呢?这背后是 CSRF 攻击。

  攻击的流程是这样的:

  1,攻击者访问 A,通过点击,触发授权流程

  2,攻击者在本地设置代理,用于拦截浏览器的请求

  3,攻击者继续完成授权流程,这时 B 会产生一个 code,并通过 redirect_uri 跳转到 A,尝试把 code 带给 A

  4,攻击者拦截掉这个 302 跳转,拿到 url,里面有 code 参数

  5,攻击者通过某种方式,诱骗真正的用户点击或触发这个 url

  6,A 收到 code,于是去兑换 access token,成功后把 access token 和用户关联起来。然而,实际上这个 access token 并不是用户自己的,而是攻击者的

  到这里攻击完成。这种手法常常用来攻击第三方登录功能。因为第三方登录通常是用 Oauth2.0 实现的。其正常流程为:

  1,用户在 A 点击第三方登录按钮,跳到 B 的授权页面

  2,用户完成授权,B 跳转回 A,带上 code

  3,A 用 code 去兑换 access token,然后再用 access token 去调 B 的api,取到 B_userid

  4,A 做一个绑定,把用户在 A 的当前账号和 B 账号绑定起来,即 B_userid -> A_userid

  5,下次用户再用 B_userid 登录,A 就知道查到对应的 A_userid,于是实现了第三方登录

  被攻击者攻击了之后,B_userid -> A_userid 这个关系被篡改掉了,里面的 B_userid 被改成了攻击者的 B 账号,而不是用户的 B 账号。也就是说,下次攻击者在 A 点第三方登录,然后登录自己的 B 账号,再进到 A 里面,就能登录进用户的 A 账号了。

  用图说明:

  正常情况下,第三方登录做的是:

Oauth2 expires_in 如何设置_安全

  被攻击者经过这样操作后,就变成:

Oauth2 expires_in 如何设置_随机字符串_02

  仔细分析这种攻击的核心关键特征,就是发起授权的用户(攻击者)和最后进行绑定的用户(真实用户)不是同一个人。那有没有办法在绑定的时候检查一下呢?

  可以的,state 参数就是用来做这个的。它本质上相当于一个一次性的临时身份证,A 调起授权流程时,为当前登录的用户(此时是攻击者)生成一个随机字符串,作为 state 的值传递给 B,B 302 返回时,把 state 原封不动的返回。由于攻击者进行拦截并诱导真实用户点击,此时当前登录的用户变成了真实用户。但是攻击者不知道真实用户的临时身份证,所以无法伪造。那么 A 在绑定的时候就可以查到 state 对应的是攻击者的账户,而不是真实用户的账户,于是拒绝做绑定。