PKCE - OAuth2 的安全措施
Table of Contents
OAuth2 是现代 Web 应用的通用登录标准,使用 OAuth2 可以将用户系统从业务系统中抽离出来,实现单点登录(SSO)。如果你还不熟悉的话,建议你去看看:
随着 Web 应用的普及,OAuth2 的使用范围也越来越广泛。在某些场景中, OAuth2 会显得不那么安全,这里介绍一个 OAuth2 在使用中的设计漏洞,和官方提出的修正措施。
1 在浏览器上打开应用
现代的操作系统,都允许应用注册一个 url,当用户在浏览器上访问这个url时,就打开这个应用。比如,在我的 windows 上,想打开 vscode,只需要访问下面的地址:
vscode://file/c:/Windows/System32/Drivers/etc/hosts
能打开 vscode 是因为 vscode 在安装时像系统注册了 “vscode://” 这个 URI,当访问这个 URI,系统会从注册表中查找对应的应用,再打开它。
2 OAuth2 流程 (Authorization Code)
当应用程序想要使用 OAuth2 时,如果不想带上一个浏览器在程序包里面,就可以使用系统自带的浏览器。跟 Web 的 OAuth2 不同的是,redirect_uri 设置成了 APP 向系统注册的 URI,这个 URI 可以直接打开 APP。流程如下图所示:
3 OAuth2 流程的缺陷
这个流程有个问题,那就是其它的 APP 也可以注册自己的 URI,它注册的 URI 跟正常的 APP 的 URI 一样的话,那么系统可能就会打开这个新注册的 APP,这个新注册的 APP 就拿到了 code,进而拿到 access_token。
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ | End Device (e.g., Smartphone) | | | | +-------------+ +----------+ | (6) Access Token +----------+ | |Legitimate | | Malicious|<--------------------| | | |OAuth 2.0 App| | App |-------------------->| | | +-------------+ +----------+ | (5) Authorization | | | | ^ ^ | Grant | | | | \ | | | | | | \ (4) | | | | | (1) | \ Authz| | | | | Authz| \ Code | | | Authz | | Request| \ | | | Server | | | \ | | | | | | \ | | | | | v \ | | | | | +----------------------------+ | | | | | | | (3) Authz Code | | | | Operating System/ |<--------------------| | | | Browser |-------------------->| | | | | | (2) Authz Request | | | +----------------------------+ | +----------+ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
4 PKCE 流程
PKCE (Proof Key for Code Exchange by OAuth Public Clients) 是官方提出的修正方案,专门针对上面提到的问题。他在 (1) 请求中加入参数 "code_challange",他是对随机数的哈希结果。在请求 token 地址的时候,再将随机数发送过去,这样 token 地址对应的服务就可以计算这个随机数的哈希,与 (1) 请求的 code_challange 对比,如果不一样就拒绝请求。
+-------------------+ | Authz Server | +--------+ | +---------------+ | | |--(A)- Authorization Request ---->| | | | | + t(code_verifier), t_m | | Authorization | | | | | | Endpoint | | | |<-(B)---- Authorization Code -----| | | | | | +---------------+ | | Client | | | | | | +---------------+ | | |--(C)-- Access Token Request ---->| | | | | + code_verifier | | Token | | | | | | Endpoint | | | |<-(D)------ Access Token ---------| | | +--------+ | +---------------+ | +-------------------+
上图中, "t(code_verifier)" 就是这个哈希值;"code_verifier" 是随机数; t_m 指的是 "code_challenge_method" 参数,指的是哈希算法,如 "S256"。
具体细节可以查看 rfc7636。
5 总结
OAuth2 的流程在 redirect_uri 不受控的情况下会有较严重的安全问题,PKCE 使用一个哈希值校验的方式解决了这个问题。目前大多数 OAuth2,或者 OIDC 服务都开启了 PKCE,如果需要用到第三方登录的话,可以试试看,到底谁没开。
其实这个方案还有一点点问题,那就是 auth request (A)虽然是 https 的,但本地 APP 可以从浏览器历史记录访问到 t(code_verifier),而这个哈希值可以用采用表匹配,运气好的话就能匹配到。本地应用里面也有一些反骨的,他们用语言自带的伪随机数(C的 rand(),python 的 random)当做生成 code_verifier,导致每次随机数都是一样的,也是会造成缺陷。但这些缺陷都是使用的问题,PKCE 流程本身是没问题的,这些细节在 rfc 里都谢了。不注意看文档的开发者,又不愿意用现有方案非要自己实现的,只能自求多福了。