我们在开发微服务的时候,多半会采用 OAuth2 进行授权认证。
本文将介绍如何通过状态随机数来防止可能的 CSRF 攻击。
CSRF
CSRF 攻击
OAuth2 中的 CSRF 攻击
OAuth2 授权过程中有以下几个参数:
response_type=code
client_id
redirect_uri
这几个参数都是公开的,所有用户都使用相同的值。
攻击者也无法控制 redirect_uri
,因为这个会配置在授权服务器的合法地址列表中。
攻击者能控制的是可选参数 state
,如果授权过程中提供了 state
,那么授权成功后会原封不动的返回 state
。
授权流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| sequenceDiagram autonumber participant A as Webapp participant C as Passport participant B as Backend
A ->> +C: redirect /login?...&state=value C ->> C: login C -->> -A: redirect /?code=...&state=value
A -->> +C: /TOKEN?code=... C -->> -A: access_token
A ->> B: Authorization: Bearer <access_token>
|
state
本身不容易受到任何攻击,但是 Web 应用程序可能会实现自定义逻辑,这种逻辑使用攻击者有机可乘。
攻击流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| sequenceDiagram autonumber participant A as Webapp participant C as Passport Actor D as Attacker participant B as Backend
D ->> +C: redirect /login?...&state=value C ->> C: login C -->> -A: redirect /?code=...&state=value
A -->> +C: /TOKEN?code=... C -->> -A: access_token
A ->> A: read state
A ->> B: CSRF Attack
|
防御
要防止这种攻击也非常简单,将 state
换成随机数,应用程序在本地妥善保存这个随机数与业务之间的关系。
在授权结束后,通过随机数判断要执行的业务,并在同时将随机数与业务对应的关系删除。
而在每次请求时都使用新的随机数,这样便能防止攻击者猜测到真正要执行的业务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| sequenceDiagram autonumber participant Webapp participant Passport Actor Attacker participant Backend
Webapp ->> +Passport: redirect /login?...&state=nonce Passport ->> Passport: login Passport -->> -Webapp: redirect /?code=...&state=nonce
Webapp -->> +Passport: /TOKEN?code=... Passport -->> -Webapp: access_token
Webapp ->> Webapp: compare state nonce with stored Webapp ->> Backend: Authorization: Bearer <access_token>
Note Over Webapp, Backend: CSRF Attack Attacker ->> +Passport: redirect /login?...&state=nonce Passport ->> Passport: login Passport -->> -Webapp: redirect /?code=...&state=nonce
Webapp -->> +Passport: /TOKEN?code=... Passport -->> -Webapp: access_token
Webapp ->> Webapp: no stored nonce Note Over Webapp: Error
|