由英文版翻译过来的
摘要
使用授权码授权类型的 OAuth 2.0 公共客户端容易受到授权码拦截攻击。本规范描述了这种攻击,并介绍了一种通过使用代码交换的密钥证明(Proof Key for Code Exchange,简称 PKCE,发音为“pixy”)来减轻这种威胁的技术。
本备忘录的状态
这是一份互联网标准跟踪文档。
本文档由互联网工程任务组 (IETF) 制定。它代表了 IETF 社区的共识。经过公开审查并获得互联网工程指导组 (IESG) 批准发布。有关互联网标准的更多信息,请参见 RFC 5741 的第 2 节。
有关本文档的当前状态、任何勘误以及如何提供反馈的信息,请访问 http://www.rfc-editor.org/info/rfc7636。
版权声明
版权所有 (c) 2015 IETF 信托组织和被确认为本文档作者的个人。保留所有权利。
本文档受 BCP 78 和 IETF 信托组织于本文档发布日期有效的“关于 IETF 文档的法律条款”(http://trustee.ietf.org/license-info)约束。请仔细阅读这些文件,因为它们描述了您与此文档相关的权利和限制。从该文档中提取的代码组件必须包含信托法律条款第 4.e 节所述的简化 BSD 许可证文本,并且按照简化 BSD 许可证中所述的“无担保”条款提供。
引言
OAuth 2.0 [RFC6749] 公共客户端容易受到授权码拦截攻击。
在这种攻击中,攻击者在未受传输层安全协议 (TLS) 保护的通信路径中拦截从授权端点返回的授权码,例如客户端操作系统内的应用程序间通信。
一旦攻击者获得授权码的访问权限,就可以使用它来获取访问令牌。
图 1 以图形方式展示了这种攻击。在步骤 (1) 中,运行在终端设备(例如智能手机)上的原生应用程序通过浏览器/操作系统发出 OAuth 2.0 授权请求。在这种情况下,重定向端点 URI 通常使用自定义 URI 方案。步骤 (1) 通过无法被拦截的安全 API 进行,尽管在高级攻击场景中可能被观察到。然后,请求在步骤 (2) 中被转发到 OAuth 2.0 授权服务器。由于 OAuth 要求使用 TLS,因此此通信受 TLS 保护,无法被拦截。授权服务器在步骤 (3) 中返回授权码。在步骤 (4) 中,授权码通过在步骤 (1) 中提供的重定向端点 URI 返回给请求者。
请注意,恶意应用程序可能除了合法的 OAuth 2.0 应用程序之外,还将自己注册为自定义方案的处理程序。一旦这样做,恶意应用程序现在就能够在步骤 (4) 中拦截授权码。这允许攻击者分别在步骤 (5) 和 (6) 中请求并获取访问令牌。
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| 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 | |
| +----------------------------+ | +----------+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+图 1:授权码拦截攻击
要使此攻击奏效,需要满足以下一些前提条件:
攻击者设法在客户端设备上注册一个恶意应用程序,并注册了一个也由另一个应用程序使用的自定义 URI 方案。操作系统必须允许由多个应用程序注册自定义 URI 方案。
使用 OAuth 2.0 授权码模式。
攻击者可以访问 OAuth 2.0 [RFC6749] 的“client_id”和“client_secret”(如果已配置)。所有 OAuth 2.0 原生应用程序客户端实例都使用相同的“client_id”。在客户端二进制应用程序中配置的密钥不能被认为是保密的。
满足以下条件之一:
4a. 攻击者(通过已安装的应用程序)只能观察来自授权端点的响应。当“code_challenge_method”值为“plain”时,仅缓解此攻击。[Sakimura 等人] 标准跟踪 [第 4 页] RFC 7636 OAUTH PKCE 2015 年 9 月
4b. 更复杂的攻击场景允许攻击者(除了响应之外)还能够观察到对授权端点的请求。然而,攻击者无法充当中间人。这是由操作系统中泄露的 http 日志信息造成的。为了缓解这种情况,“code_challenge_method”值必须设置为“S256”或由密码学安全的“code_challenge_method”扩展定义的值。
更详细的解释:
这段文字描述了授权码拦截攻击的先决条件,以及 PKCE 如何缓解这些攻击。以下是更详细的解释:
恶意应用程序注册相同的 URI 方案: 这是攻击的核心。OAuth 使用自定义 URI 方案(例如 myapp://callback)将授权码重定向回应用程序。如果恶意应用程序也注册了相同的 URI 方案,操作系统会将授权码错误地发送给恶意应用程序。
使用 OAuth 2.0 授权码模式: 这种攻击只针对使用授权码模式的 OAuth 2.0 客户端。
访问 client_id 和 client_secret: 在传统的 OAuth 2.0 中,client_secret 用于验证客户端的身份。但是,对于原生应用程序(例如移动应用程序),client_secret 无法安全地存储在客户端设备上,因此通常被认为是公开的。攻击者可以轻易获取 client_id,而即使有 client_secret,也无法有效防止攻击。
两种攻击场景和相应的缓解措施:
4a. 仅观察响应: 这是相对简单的攻击场景,攻击者只能拦截授权服务器发送给客户端的响应(包含授权码)。当 code_challenge_method 设置为 “plain” 时,PKCE 只能缓解这种攻击。这意味着在请求中发送的 code_challenge 是未经过任何hash处理的明文。这种方式的安全性很弱,基本不使用。
4b. 观察请求和响应(但不充当中间人): 这是更复杂的攻击场景,攻击者不仅可以拦截响应,还可以通过操作系统中泄露的 HTTP 日志等方式观察到客户端发送给授权服务器的请求。在这种情况下,仅仅使用 “plain” 的 code_challenge_method 是不够的。为了缓解这种更高级的攻击,code_challenge_method 必须设置为 “S256”(使用 SHA-256 哈希算法)或使用其他密码学安全的扩展方法。S256通过对code_verifier进行hash处理,即使请求被截获,攻击者也无法轻易的通过code_challenge反推出code_verifier。
虽然这是一份冗长的前提条件列表,但所描述的攻击已经在现实中被观察到,必须在 OAuth 2.0 部署中加以考虑。虽然 OAuth 2.0 威胁模型(RFC6819 的第 4.4.1 节)描述了缓解技术,但不幸的是,它们不适用,因为它们依赖于每个客户端实例的密钥或每个客户端实例的重定向 URI。
为了缓解这种攻击,此扩展利用了一个动态创建的加密随机密钥,称为“代码验证器”。每个授权请求都会创建一个唯一的代码验证器,并将它的转换值(称为“代码挑战”)发送到授权服务器以获取授权码。然后,将获得的授权码与“代码验证器”一起发送到令牌端点,服务器将其与先前接收到的请求代码进行比较,以便它可以执行客户端对“代码验证器”的所有权证明。这是有效的缓解措施,因为攻击者不知道这个一次性密钥,因为它通过 TLS 发送并且无法被拦截。
简要说明
这段话描述了一种针对 OAuth 2.0 授权流程的攻击,并介绍了一种缓解措施。攻击者可以通过拦截授权请求和重定向 URI 来获取授权码,从而冒充客户端获取访问令牌。
缓解措施是引入一个动态生成的随机密钥“代码验证器”。客户端在发起授权请求时生成这个密钥,并将它的转换值(“代码挑战”)发送给授权服务器。服务器在验证客户端身份时,会要求客户端提供原始的“代码验证器”。由于“代码验证器”是随机生成的且只在一次请求中使用,攻击者无法获取它,从而无法伪造授权请求。
关键概念
OAuth 2.0: 一种授权协议,允许第三方应用程序访问用户在其他网站或服务上的数据。 授权码: 用于交换访问令牌的临时代码。 代码验证器: 一个随机生成的密钥,用于验证客户端的身份。 代码挑战: 代码验证器的转换值。 TLS: 传输层安全协议,用于加密网络通信。
1.1. 协议流程
+-------------------+
| 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 ---------| | |
+--------+ | +---------------+ |
+-------------------+图2:抽象协议流程
本规范为 OAuth 2.0 的授权请求和访问令牌请求添加了额外的参数,概述如下(见图 2):
A. 客户端创建并记录一个称为 “code_verifier” 的秘密,并生成一个经过转换的版本 “t(code_verifier)“(称为 “code_challenge”),然后在 OAuth 2.0 授权请求中连同转换方法 “t_m” 一起发送。 B. 授权端点按常规方式响应,但会记录 “t(code_verifier)” 和转换方法。 C. 客户端在访问令牌请求中,按照常规方式发送授权码,同时包含在步骤 A 中生成的 “code_verifier” 秘密。 D. 授权服务器将 “code_verifier” 转换后与步骤 B 中记录的 “t(code_verifier)” 进行比较。如果两者不相等,则拒绝访问。 防护机制 攻击者即使在步骤 B 截获了授权码,也无法利用其兑换访问令牌,因为他们并不拥有 “code_verifier” 这一秘密。
2. 符号约定
本文档中的关键字 “MUST”(必须)、“MUST NOT”(绝对禁止)、“REQUIRED”(需要)、“SHALL”(应当)、“SHALL NOT”(不应当)、“SHOULD”(建议)、“SHOULD NOT”(不建议)、“RECOMMENDED”(推荐)、“NOT RECOMMENDED”(不推荐)、“MAY”(可以)和 “OPTIONAL”(可选)应按照 [RFC2119] 中所述进行解释。如果这些词未以大写形式书写,则应根据其自然语言的含义进行解释。
本规范使用 [RFC5234] 中定义的扩展巴科斯-诺尔范式(ABNF)符号表示法。
STRING 表示由零个或多个 ASCII 字符 [RFC20] 组成的序列。 OCTETS 表示由零个或多个字节组成的序列。 ASCII(STRING) 表示 STRING 的 ASCII 表示形式 [RFC20] 所对应的字节序列,其中 STRING 是由零个或多个 ASCII 字符组成的序列。 BASE64URL-ENCODE(OCTETS) 表示根据附录 A 对 OCTETS 进行 base64url 编码的结果,并生成一个 STRING。 BASE64URL-DECODE(STRING) 表示根据附录 A 对 STRING 进行 base64url 解码的结果,生成一个字节序列(OCTETS)。 SHA256(OCTETS) 表示对 OCTETS 进行 SHA2 256 位哈希计算的结果 [RFC6234]。
3. 术语
除了 OAuth 2.0 [RFC6749] 中定义的术语外,本规范还定义了以下术语:
code verifier(代码验证器) 一个加密随机字符串,用于将授权请求与令牌请求相关联。
code challenge(代码挑战) 从代码验证器派生的一个挑战值,在授权请求中发送,用于后续验证。
code challenge method(代码挑战方法) 用于派生代码挑战值的方法。
Base64url Encoding(Base64url 编码) 一种使用 [RFC4648] 第 5 节中定义的 URL 和文件名安全字符集的 Base64 编码方式,其中省略了所有尾部的 = 字符(根据 [RFC4648] 第 3.2 节的规定),并且不包含任何换行符、空白字符或其他附加字符。(有关实现无填充的 Base64url 编码的说明,请参见附录 A。)
3.1. 缩略语
ABNF:扩展巴科斯-诺尔范式(Augmented Backus-Naur Form) Authz:授权(Authorization) PKCE:用于代码交换的证明密钥(Proof Key for Code Exchange) MITM:中间人攻击(Man-in-the-middle) MTI:必须实现(Mandatory To Implement)
4协议
4.1. 客户端创建一个代码验证器
客户端首先为每个 OAuth 2.0 [RFC6749] 授权请求创建一个代码验证器(“code_verifier”),其创建方式如下:
代码验证器(code_verifier)是一个高熵加密随机字符串,使用 [RFC3986] 第 2.3 节中定义的非保留字符 [A-Z] / [a-z] / [0-9] / ”-” / ”.” / ”_” / ”~“,其最小长度为 43 个字符,最大长度为 128 个字符。
“code_verifier” 的 ABNF(扩展巴科斯-诺尔范式)如下:
code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39注:代码验证器应具有足够的熵,使得猜测其值变得不切实际。建议使用合适的随机数生成器生成一个 32 字节的序列,然后将该字节序列进行 base64url 编码,生成一个 43 字节的 URL 安全字符串作为代码验证器。
4.2. 客户端创建代码挑战
客户端然后从代码验证器派生出一个代码挑战(code_challenge),通过对代码验证器应用以下变换之一来创建代码挑战:
plain code_challenge = code_verifier
S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
如果客户端能够使用 “S256” 变换,则必须使用 “S256”,因为 “S256” 是服务器上必须实现的(MTI)。客户端仅在无法支持 “S256”(因某些技术原因)并且通过带外配置知道服务器支持 “plain” 时,才可以使用 “plain”。
“plain” 变换用于与现有部署兼容,并且用于那些无法使用 S256 变换的受限环境。
“code_challenge” 的 ABNF 如下:
code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
4.3. 客户端发送代码挑战与授权请求一起
客户端将代码挑战作为 OAuth 2.0 授权请求的一部分发送(参见 [RFC6749] 第 4.1.1 节),并使用以下附加参数:
code_challenge 必需。代码挑战。
code_challenge_method 可选,如果请求中未包含此参数,则默认为 “plain”。代码验证器的变换方法可以是 “S256” 或 “plain”。
4.4. 服务器返回代码
当服务器发出授权代码时,它必须将 “code_challenge” 和 “code_challenge_method” 值与授权代码关联,以便后续验证。
通常,“code_challenge” 和 “code_challenge_method” 值会以加密形式存储在授权代码中,但也可以选择将其存储在与代码关联的服务器上。服务器不得以其他实体可以提取的形式将 “code_challenge” 值包含在客户端请求中。
服务器将 “code_challenge” 与发出的授权代码关联的具体方法超出了本规范的范围。
4.4.1. 错误响应
如果服务器要求 OAuth 公共客户端使用授权码交换的证明密钥(PKCE),而客户端在请求中没有发送 “code_challenge”,则授权端点必须返回授权错误响应,并将 “error” 值设置为 “invalid_request”。此时,“error_description” 或 “error_uri” 的响应应当解释错误的性质,例如:需要代码挑战。
如果支持 PKCE 的服务器不支持请求的变换方法,则授权端点必须返回授权错误响应,并将 “error” 值设置为 “invalid_request”。此时,“error_description” 或 “error_uri” 的响应应当解释错误的性质,例如:不支持的变换算法。
4.5. 客户端将授权代码和代码验证器发送到令牌端点
在收到授权代码后,客户端向令牌端点发送访问令牌请求。除了 OAuth 2.0 访问令牌请求中定义的参数(参见 [RFC6749] 第 4.1.3 节)外,还需要发送以下参数:
code_verifier 必需。代码验证器。 当授权代码被发出时,“code_challenge_method” 会与授权代码绑定在一起。令牌端点必须使用该方法来验证 “code_verifier”。
4.6. 服务器在返回令牌前验证 code_verifier
当服务器在令牌端点接收到请求时,它会通过以下步骤验证:
1.计算代码挑战:服务器根据接收到的 code_verifier 计算代码挑战(code_challenge),并与之前关联的 code_challenge 进行比较。计算时,首先根据客户端指定的 code_challenge_method 方法对 code_verifier 进行变换。
2.如果 code_challenge_method 是 “S256”:
服务器使用 SHA-256 对 code_verifier 进行哈希计算,然后进行 Base64 URL 编码。 然后将编码后的结果与原始的 code_challenge 进行比较,公式为:
BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
3.如果 code_challenge_method 是 “plain”:
直接比较 code_verifier 和 code_challenge,即:
code_verifier == code_challenge4.如果比较结果相等:
如果计算出来的 code_challenge 与接收到的 code_challenge 相等,令牌端点继续正常处理请求,按照 OAuth 2.0 [RFC6749] 规范返回令牌。 5.如果比较结果不相等:
如果 code_verifier 与 code_challenge 不匹配,服务器必须返回一个错误响应,错误类型为 “invalid_grant”,并遵循 [RFC6749] 第 5.2 节中定义的错误响应格式。 总结来说,服务器在收到令牌请求时,会使用客户端提供的 code_verifier 重新计算代码挑战,并与之前存储的 code_challenge 比较,以确保请求的合法性。如果验证通过,则继续处理令牌请求;如果验证失败,则返回 “invalid_grant” 错误。
5.兼容性
本规范的服务器实现可以接受不实现此扩展的 OAuth 2.0 客户端。如果在授权请求中没有接收到 code_verifier,则支持向后兼容性的服务器将恢复使用不包含此扩展的 OAuth 2.0 [RFC6749] 协议。
由于 OAuth 2.0 [RFC6749] 的服务器响应在本规范中没有发生变化,因此实现本规范的客户端不需要知道服务器是否实现了本规范,客户端应当按照第 4 节的定义向所有服务器发送额外的参数。
关键点总结: 向后兼容性:支持 PKCE 扩展的服务器也能够接受不使用 PKCE 的 OAuth 2.0 客户端。当没有接收到 code_verifier 时,服务器将回退到标准的 OAuth 2.0 协议。 客户端实现:客户端不需要关心服务器是否支持 PKCE 扩展,客户端应当始终发送与 PKCE 相关的参数(例如 code_verifier 和 code_challenge),以确保最大兼容性。 服务器响应未改变:服务器的响应格式与传统的 OAuth 2.0 协议兼容,不受 PKCE 扩展的影响。
6.IANA 考虑事项
根据本文档,IANA 已进行以下注册。
6.1. OAuth 参数注册表
本规范在 IANA 的 “OAuth 参数” 注册表中注册了以下参数,该注册表在 OAuth 2.0 [RFC6749] 中定义。
参数名称:code_verifier
参数使用位置:令牌请求 变更控制者:IESG 规范文档:RFC 7636(本文档) 参数名称:code_challenge
参数使用位置:授权请求 变更控制者:IESG 规范文档:RFC 7636(本文档) 参数名称:code_challenge_method
参数使用位置:授权请求 变更控制者:IESG 规范文档:RFC 7636(本文档)
6.2. PKCE 代码挑战方法注册表
本规范建立了 “PKCE 代码挑战方法” 注册表。新的注册表应作为 “OAuth 参数” 注册表的子注册表。
对于授权端点使用的额外 code_challenge_method 类型的注册,采用 规范要求(Specification Required)策略进行注册 [RFC5226],该策略包括由一个或多个指定专家(Designated Experts, DEs)审查请求。DEs 将确保请求在 oauth-ext-review@ietf.org 邮件列表上至少经过两周的审查,并确保该邮件列表中的讨论已经收敛,才能对请求作出回应。为了在发布前分配值,指定专家可以在确认可接受的规范将发布后批准注册。
注册请求和讨论应使用适当的主题,例如 “Request for PKCE code_challenge_method: example”。
指定专家在评估注册请求时,应考虑邮件列表上的讨论,以及挑战方法的整体安全性。新的方法不应在授权请求中透露 code_verifier 的值。对于拒绝的请求,专家应提供解释,并在适当的情况下提供如何使请求成功的建议。
6.2.1. 注册模板
代码挑战方法参数名称:
请求的名称(例如,“example”)。由于本规范的核心目标之一是使得最终的表示形式尽可能简洁,因此建议该名称尽量简短 —— 不要超过 8 个字符,除非有充分理由这么做。该名称是区分大小写的。名称不得与其他已注册名称在大小写上相同,除非指定专家声明在特定情况下允许此例外。 变更控制者:
对于标准跟踪的 RFC,写为 “IESG”。对于其他情况,给出负责方的名称。其他详细信息(例如,邮寄地址、电子邮件地址和主页 URI)也可以包括。 规范文档:
指定该参数的文档参考,最好包括可以用于检索文档副本的 URI。也可以包括相关章节的指示,但这不是必需的。
6.2.2. 初始注册表内容
根据本文档,IANA 已在此注册表中注册了第 4.2 节定义的代码挑战方法参数名称。
代码挑战方法参数名称:plain
变更控制者:IESG 规范文档:RFC 7636 第 4.2 节(本文档) 代码挑战方法参数名称:S256
变更控制者:IESG 规范文档:RFC 7636 第 4.2 节(本文档)
7.安全性考虑
7.1. code_verifier 的熵
安全模型依赖于攻击者无法猜测或获知 code_verifier。确保这一点是至关重要的。因此,code_verifier 必须以一种加密随机且具有高熵的方式生成,使得攻击者无法实际猜测出其值。
客户端应生成一个至少具有 256 位熵的 code_verifier。这可以通过使用适当的随机数生成器生成一个 32 字节的序列来实现。然后,可以将该字节序列进行 base64url 编码,以生成一个 43 字节的 URL 安全字符串,作为具有所需熵的 code_challenge。
7.2. 防止窃听者
客户端不得在尝试了 “S256” 方法后降级为 “plain” 方法。支持 PKCE 的服务器必须支持 “S256” 方法,而不支持 PKCE 的服务器将简单地忽略未知的 code_verifier。因此,当呈现 “S256” 时发生错误只能意味着服务器存在问题,或者是中间人攻击(MITM)攻击者尝试进行降级攻击。
“S256” 方法能有效防止窃听者观察或拦截 “code_challenge”,因为挑战值不能在没有 code_verifier 的情况下使用。而在 “plain” 方法下,攻击者有可能通过设备或 HTTP 请求观察到 “code_challenge”。由于在这种情况下,“code_challenge” 与 code_verifier 相同,“plain” 方法无法防止初始请求中的窃听。
使用 “S256” 方法可以防止 code_verifier 值泄露给攻击者。
因此,“plain” 方法不应使用,仅用于与已经部署的实现兼容,其中请求路径已被保护。在新的实现中,除非由于某些技术原因无法支持 “S256”,否则不应使用 “plain” 方法。
应使用 “S256” 或其他加密安全的代码挑战方法扩展。“plain” 代码挑战方法依赖于操作系统和传输安全性来防止攻击者泄露请求。
如果代码挑战方法是 “plain” 并且代码挑战需要被返回到授权 “code” 中以实现无状态服务器,则必须以加密方式处理,使得只有服务器可以解密并提取该挑战值。
7.3. 对 code_challenge 进行加盐
为了减少实现复杂度,在生成代码挑战时并未使用加盐,因为 code_verifier 已包含足够的熵来防止暴力破解攻击。将一个公开已知的值与一个包含 256 位熵的 code_verifier 连接起来,然后使用 SHA256 进行哈希处理以生成 code_challenge,并不会增加暴力破解有效 code_verifier 值所需的尝试次数。
虽然 “S256” 转换方式类似于对密码进行哈希处理,但两者之间存在重要区别。密码通常是相对低熵的单词,可以在离线进行哈希计算,并通过查找字典获取哈希值。通过将一个独特但公开的值与每个密码连接起来再进行哈希,可以极大地扩展攻击者需要搜索的字典空间。
现代图形处理器(GPU)使得攻击者能够实时计算哈希值,其速度比从磁盘查找哈希值还要快。这消除了加盐在增加低熵密码的暴力破解攻击复杂度方面的价值。
7.4. OAuth 安全性考虑
所有在 [RFC6819] 中提出的 OAuth 安全分析都适用,因此读者应仔细遵循该文档中的建议。
7.5. TLS 安全性考虑
当前的安全性考虑可以在《传输层安全性(TLS)和数据报传输层安全性(DTLS)的安全使用建议》[BCP195] 中找到。该文档替代了 OAuth 2.0 [RFC6749] 中关于 TLS 版本的建议。
8.参考文献
8.1. 规范性参考文献
[BCP195] Sheffer, Y., Holz, R., and P. Saint-Andre, “Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)”, BCP 195, RFC 7525, May 2015, http://www.rfc-editor.org/info/bcp195 .
[RFC20] Cerf, V., “ASCII format for network interchange”, STD 80, RFC 20, DOI 10.17487/RFC0020, October 1969, http://www.rfc-editor.org/info/rfc20 .
[RFC2119] Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, http://www.rfc-editor.org/info/rfc2119 .
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, “Uniform Resource Identifier (URI): Generic Syntax”, STD 66, RFC 3986, DOI 10.17487/RFC3986, January 2005, http://www.rfc-editor.org/info/rfc3986 .
[RFC4648] Josefsson, S., “The Base16, Base32, and Base64 Data Encodings”, RFC 4648, DOI 10.17487/RFC4648, October 2006, http://www.rfc-editor.org/info/rfc4648 .
[RFC5226] Narten, T. and H. Alvestrand, “Guidelines for Writing an IANA Considerations Section in RFCs”, BCP 26, RFC 5226, DOI 10.17487/RFC5226, May 2008, http://www.rfc-editor.org/info/rfc5226 .
[RFC5234] Crocker, D., Ed. and P. Overell, “Augmented BNF for Syntax Specifications: ABNF”, STD 68, RFC 5234, DOI 10.17487/RFC5234, January 2008, http://www.rfc-editor.org/info/rfc5234 .
[RFC6234] Eastlake 3rd, D. and T. Hansen, “US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)”, RFC 6234, DOI 10.17487/RFC6234, May 2011, http://www.rfc-editor.org/info/rfc6234 .
[RFC6749] Hardt, D., Ed., “The OAuth 2.0 Authorization Framework”, RFC 6749, DOI 10.17487/RFC6749, October 2012, http://www.rfc-editor.org/info/rfc6749 .
8.2. 参考性参考文献
[RFC6819] Lodderstedt, T., Ed., McGloin, M., and P. Hunt, “OAuth 2.0 Threat Model and Security Considerations”, RFC 6819, DOI 10.17487/RFC6819, January 2013, http://www.rfc-editor.org/info/rfc6819 .
附录 A. 实现无填充 Base64url 编码的注意事项
本附录描述了如何实现一个基于标准 Base64 编码函数的 Base64url 编码函数,且不使用填充。
为具体说明,下面给出了示例 C# 代码来实现这些函数。其他编程语言也可以使用类似的代码。
static string base64urlencode(byte[] arg)
{
string s = Convert.ToBase64String(arg); // 常规的 Base64 编码器
s = s.Split('=')[0]; // 去除尾部的 '='
s = s.Replace('+', '-'); // Base64 编码中的第 62 个字符
s = s.Replace('/', '_'); // Base64 编码中的第 63 个字符
return s;
}
示例:未编码与已编码值之间的对应关系 以下是一个未编码与已编码值之间的示例。这个八位字节序列在编码后,得到下面的字符串,解码后可以重建回该八位字节序列。
原始字节序列(未编码): 3 236 255 224 193 编码后的字符串(Base64url 编码): A-z_4ME 这个过程可以帮助开发者在实现 OAuth 2.0 PKCE 时,处理 Base64url 编码,并确保编码结果符合标准要求(不使用填充)。
附录 B. S256 code_challenge_method 示例
本附录展示了使用 S256 方法生成 code_challenge 和验证过程的具体示例。
步骤 1: 生成 code_verifier 客户端使用适当的随机数生成器生成一个 32 字节的序列。此示例中的字节序列(使用 JSON 数组表示)为:
[116, 24, 223, 180, 151, 153, 224, 37, 79, 250, 96, 125, 216, 173, 187, 186, 22, 212, 37, 77, 105, 214, 191, 240, 91, 88, 5, 88, 83, 132, 141, 121]
将此字节序列进行 Base64url 编码,得到 code_verifier:
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk步骤 2: 计算 code_challenge 接下来,客户端使用 SHA256 哈希函数对 code_verifier 进行哈希运算,得到以下字节序列:
[19, 211, 30, 150, 26, 26, 216, 236, 47, 22, 177, 12, 76, 152, 46, 8, 118, 168, 120, 173, 109, 241, 68, 86, 110, 225, 137, 74, 203, 112, 249, 195]
对这个字节序列进行 Base64url 编码,得到 code_challenge:
E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
步骤 3: 在授权请求中包含 code_challenge 和 code_challenge_method 授权请求将包括以下参数:
code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
授权服务器记录下 code_challenge 和 code_challenge_method,以及授予给客户端的授权码。
步骤 4: 客户端在请求令牌时提供 code_verifier 在向令牌端点的请求中,客户端将提供在授权响应中收到的授权码,以及额外的 code_verifier 参数:
code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
步骤 5: 授权服务器验证 code_verifier 授权服务器从记录中获取授权码的相关信息。根据记录的 code_challenge_method 为 S256,授权服务器接下来对 code_verifier 的值进行 SHA256 哈希,并进行 Base64url 编码:
BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
将计算结果与 code_challenge 进行比较:
BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
步骤 6: 响应令牌或返回错误 如果两个值相等,授权服务器会继续处理请求,并返回令牌,只要请求中没有其他错误。 如果两个值不相等,则请求被拒绝,并返回错误。 通过这个过程,OAuth 2.0 的 PKCE(Proof Key for Code Exchange)能够提供更强的安全性,防止中间人攻击和授权码窃取。
致谢
本规范的初版草案由 OpenID 基金会的 OpenID AB/Connect 工作组创建。
该规范是 OAuth 工作组的成果,工作组成员包括几十位积极参与和全心投入的成员。特别地,以下个人为形成最终规范提供了思想、反馈和措辞的贡献:
Anthony Nadalin, Microsoft Axel Nenker, Deutsche Telekom Breno de Medeiros, Google Brian Campbell, Ping Identity Chuck Mortimore, Salesforce Dirk Balfanz, Google Eduardo Gueiros, Jive Communications Hannes Tschonfenig, ARM James Manger, Telstra Justin Richer, MIT Kerberos Josh Mandel, Boston Children’s Hospital Lewis Adam, Motorola Solutions Madjid Nakhjiri, Samsung Michael B. Jones, Microsoft Paul Madsen, Ping Identity Phil Hunt, Oracle Prateek Mishra, Oracle Ryo Ito, mixi Scott Tomilson, Ping Identity Sergey Beryozkin Takamichi Saito Torsten Lodderstedt, Deutsche Telekom William Denniss, Google 这些贡献帮助完善了本规范,推动了其最终版本的完成。
作者联系方式
Nat Sakimura (编辑) Nomura Research Institute 1-6-5 Marunouchi, Marunouchi Kitaguchi Bldg. Chiyoda-ku, Tokyo 100-0005 Japan
电话: +81-3-5533-2111 电子邮件: n-sakimura@nri.co.jp 网址: http://nat.sakimura.org/
S256 code_challenge_method 示例 C#