state参数 , 在OAuth 2.0核心协议中 , 通过code换取token步骤中 , 推荐使用state参数 , 把请求和响应关联起来 , 可以防止跨站点请求伪造-CSRF攻击 , 但是state并不能防止上面的授权码拦截攻击 , 因为请求和响应并没有被伪造 , 而是响应的授权码被恶意程序拦截 。
PKCE 协议流程
文章图片
PKCE协议本身是对OAuth 2.0的扩展 , 它和之前的授权码流程大体上是一致的 , 区别在于 , 在向授权服务器的authorize endpoint请求时 , 需要额外的code_challenge和code_challenge_method参数 , 向token endpoint请求时 , 需要额外的code_verifier参数 , 最后授权服务器会对这三个参数进行对比验证 , 通过后颁发令牌 。
code_verifier
对于每一个OAuth授权请求 , 客户端会先创建一个代码验证器code_verifier , 这是一个高熵加密的随机字符串 , 使用URI非保留字符 (Unreserved characters) , 范围[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" , 因为非保留字符在传递时不需要进行URL 编码 , 并且 code_verifier 的长度最小是43 , 最大是128 , code_verifier要具有足够的熵它是难以猜测的 。
code_verifier的扩充巴科斯范式 (ABNF) 如下:
code-verifier= 43* 128unreservedunreserved= ALPHA / DIGIT / "-"/ "."/ "_"/ "~" ALPHA= %x41- 5A / %x61- 7ADIGIT= %x30- 39 简单点说就是在[A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"范围内 , 生成43-128位的随机字符串 。
java 示例
// Required: Node.js crypto module // https://nodejs.org/api/crypto.html#crypto_crypto functionbase64URLEncode( str) {returnstr.toString( 'base64').replace( /\+/g ,'-').replace( /\//g ,'_').replace( /=/g ,'');} varverifier = base64URLEncode(crypto.randomBytes( 32));java 示例
// Required: Apache Commons Codec // https://commons.apache.org/proper/commons-codec/ // Import the Base64 class. // import org.apache.commons.codec.binary.Base64; SecureRandom sr = newSecureRandom;byte[] code = newbyte[ 32];sr.nextBytes(code); String verifier = Base64.getUrlEncoder.withoutPadding.encodeToString(code); c# 示例
publicstaticstringrandomDataBase64url( intlength ){ RNGCryptoServiceProvider rng = newRNGCryptoServiceProvider;byte[] bytes = newbyte[length];rng.GetBytes(bytes); returnbase64urlencodeNoPadding(bytes);} publicstaticstringbase64urlencodeNoPadding( byte[] buffer ) {stringbase64 = Convert.ToBase64String(buffer); base64 = base64.Replace( "+" ,"-"); base64 = base64.Replace( "/" ,"_"); base64 = base64.Replace( "=" ,""); returnbase64; }
stringcode_verifier = randomDataBase64url( 32);
code_challenge_method
对code_verifier进行转换的方法 , 这个参数会传给授权服务器 , 并且授权服务器会记住这个参数 , 颁发令牌的时候进行对比 , code_challenge == code_challenge_method(code_verifier), 若一致则颁发令牌 。
code_challenge_method可以设置为plain(原始值)或者S256(sha256哈希) 。
code_challenge
使用code_challenge_method对code_verifier进行转换得到code_challenge , 可以使用下面的方式进行转换
- plain code_challenge = code_verifier
- S256 code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
客户端应该首先考虑使用S256进行转换 , 如果不支持 , 才使用plain , 此时 code_challenge和code_verifier的值相等 。
java 示例
特别声明:本站内容均来自网友提供或互联网,仅供参考,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。