Hello everyone, I am a laboratory researcher in this issue-wait for it to get dark. Today our research object is the OAuth extended protocol PKCE. In the OAuth 2.1 draft, the authorization model of Authorization Code + PKCE is recommended. Why is PKCE so important? Next, let us go to the laboratory to find out!
Foreword
The full name of PKCE is Proof Key for Code Exchange. It was released in 2015. It is an extended protocol at the core of OAuth 2.0, so it can be used in conjunction with existing authorization modes, such as Authorization Code + PKCE. This is also the best practice. PKCE was originally Created for mobile device applications and local applications, mainly to reduce the authorization code interception attacks of public clients.
In the latest OAuth 2.1 specification (draft), it is recommended that all clients use PKCE, not just the public client, and the Implicit and Password modes are removed. What about the clients that used these two modes before? ? Yes, you can now try to use the authorization mode of Authorization Code + PKCE. So why does PKCE have this magic? In fact is that the client provides a self-created certificate to the authorization server, and the authorization server verifies the client through it, and issues the access token (access_token) to the real client. Instead of the fake .
Client type
As mentioned above, PKCE is mainly to reduce the authorization code interception attacks of public clients, so it is necessary to introduce the next two client types.
OAuth 2.0 core specification defines two client types , confidential, and public. The way to distinguish between these two types is to determine whether the client has the ability to maintain its own confidentiality credential client_secret.
- confidential
For an ordinary web site, although the user can access the front-end page, the data comes from the back-end api service of the server. The front-end just obtains the authorization code code, and the step of exchanging the code for access_token is done in the back-end api. It is an internal server. The client has the ability to maintain password or key information. This is a confidential client. - public
The client itself does not have the ability to save key information, such as desktop software, mobile apps, and single-page programs (SPA). Because these applications are published, there is actually no security at all. Malicious attackers can use decompilation and other means. View the client's key, which is a public client.
In the OAuth 2.0 authorization code mode (Authorization Code), when the client obtains the access token (access_token) from the authorization server through the authorization code code, it also needs to carry the client secret (client_secret) in the request, and the authorization server performs Verification, to ensure that access_token is issued to a legitimate client. For public clients, there is a risk of key leakage. Therefore, the authorization code mode of conventional OAuth 2.0 cannot be used. Therefore, the client_secret cannot be used. The scene, derived from Implicit implicit mode, this mode is not safe from the beginning. After a period of time, the PKCE extended protocol was launched to solve the authorization security problem of public clients.
authorization code interception attack
The above is a complete process model OAuth 2.0 authorization code, authorization code block attacks that figure C step occurs, that is, when the authorization server returns an authorization code to the client, so many steps C step Why is it unsafe ? In the OAuth 2.0 core specification, the anthorize endpoint and token endpoint of the authorization server must be protected by TLS (Secure Transport Layer Protocol), but when the authorization server carries the authorization code code back to the callback address of the client, it may not be affected by TLS Protection, malicious programs can intercept the authorization code code in this process. After getting the code, the next step is to exchange the code for the access token access_token to the authorization server. For confidential clients, the client’s secret is required when requesting access_token. The key is client_secret, and the key is stored on the back-end server, so it is useless for malicious programs to obtain the authorization code code through interception. For public clients (mobile apps, desktop apps), they have no ability to protect client_secret, because You can get the client_secret by decompilation and other means, and you can exchange the authorization code for access_token. At this point, the malicious application can request the resource server with the token.
State parameter, in the OAuth 2.0 core protocol, in the step of exchanging token by code, it is recommended to use the state parameter to associate the request and response to prevent cross-site request forgery-CSRF attacks, but state does not prevent the authorization code interception attack above , Because the request and response were not forged, but the authorization code of the response was intercepted by a malicious program.
PKCE protocol flow
The PKCE protocol itself is an extension of OAuth 2.0. It is basically the same as the previous authorization code process. The difference is that when requesting the authorize endpoint of the authorization server, additional code_challenge
and code_challenge_method
are required. When requesting the token endpoint, Additional code_verifier
parameters are required. Finally, the authorization server will compare and verify these three parameters and issue a token after passing.
code_verifier
For each OAuth authorization request, the client will first create a code verifier code_verifier, which is a high-entropy encrypted random string, using URI unreserved characters (Unreserved characters), range [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
, because it is not reserved
Characters do not need to be URL-encoded when they are transmitted, and the minimum length of code_verifier is 43, and the maximum is 128. It is difficult to guess if code_verifier has enough entropy.
The extended Backus Normal Form (ABNF) of code_verifier is as follows:
code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
Simple point that is in [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
the range, generates a random string of bits 43-128.
javascript example
// Required: Node.js crypto module
// https://nodejs.org/api/crypto.html#crypto_crypto
function base64URLEncode(str) {
return str.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
var verifier = base64URLEncode(crypto.randomBytes(32));
java example
// Required: Apache Commons Codec
// https://commons.apache.org/proper/commons-codec/
// Import the Base64 class.
// import org.apache.commons.codec.binary.Base64;
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
String verifier = Base64.getUrlEncoder().withoutPadding().encodeToString(code);
c# example
public static string randomDataBase64url(int length)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[length];
rng.GetBytes(bytes);
return base64urlencodeNoPadding(bytes);
}
public static string base64urlencodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
base64 = base64.Replace("=", "");
return base64;
}
string code_verifier = randomDataBase64url(32);
code_challenge_method
The method of converting code_verifier, this parameter will be passed to the authorization server, and the authorization server will remember this parameter, and compare it when issuing the token, code_challenge == code_challenge_method(code_verifier)
, if they are consistent, the token will be issued.
The code_challenge_method can be set to plain (original value) or S256 (sha256 hash).
code_challenge
Use code_challenge_method to convert code_verifier to get code_challenge, you can use the following method to convert
- plain
code_challenge = code_verifier - S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
The client should first consider using S256 for conversion. If it does not support it, then use plain. At this time, the values of code_challenge and code_verifier are equal.
javascript example
// Required: Node.js crypto module
// https://nodejs.org/api/crypto.html#crypto_crypto
function sha256(buffer) {
return crypto.createHash('sha256').update(buffer).digest();
}
var challenge = base64URLEncode(sha256(verifier));
java example
// Dependency: Apache Commons Codec
// https://commons.apache.org/proper/commons-codec/
// Import the Base64 class.
// import org.apache.commons.codec.binary.Base64;
byte[] bytes = verifier.getBytes("US-ASCII");
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes, 0, bytes.length);
byte[] digest = md.digest();
String challenge = Base64.encodeBase64URLSafeString(digest);
C# Example
public static string base64urlencodeNoPadding(byte[] buffer)
{
string base64 = Convert.ToBase64String(buffer);
base64 = base64.Replace("+", "-");
base64 = base64.Replace("/", "_");
base64 = base64.Replace("=", "");
return base64;
}
[InternetShortcut]
URL=https://segmentfault.com/a/1190000041093435/edit###
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
principle analysis
We mentioned the authorization code interception attack above. It means that in the entire authorization process, you only need to intercept the authorization code code that is called back from the authorization server to the client, and then you can go to the authorization server to apply for a token, because the client is public. Even if there is a key client_secret, it is useless. After the malicious program gets the access token, it can openly request the resource server.
PKCE do it ? Since the fixed client_secret is insecure, a random key (code_verifier) is generated every time a request is made. When the first request is made to the authorize endpoint of the authorization server, the code_challenge and code_challenge_method are carried. That is, the converted value and conversion method of code_verifier. Then the authorization server needs to cache these two parameters. When the second request to the token endpoint, it carries the original value of the generated random key (code_verifier), and then the authorization server uses the following The method to verify:
- plain
code_challenge = code_verifier - S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
The token is issued after passing, how should the two requests to the authorization server authorize endpoint and token endpoint be related? The authorization code code is enough, so even if the malicious program intercepts the authorization code code, but there is no code_verifier, It is also impossible to obtain an access token. Of course, PKCE can also be used on a confidential client, that is, the dual key of client_secret + code_verifier.
Finally, look at an example of request parameters:
GET /oauth2/authorize
https://www.authorization-server.com/oauth2/authorize?
response_type=code
&client_id=s6BhdRkqt3
&scope=user
&state=8b815ab1d177f5c8e
&redirect_uri=https://www.client.com/callback
&code_challenge_method=S256
&code_challenge=FWOeBX6Qw_krhUE2M0lOIH3jcxaZzfs5J4jtai5hOX4
POST /oauth2/token
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
https://www.authorization-server.com/oauth2/token?
grant_type=authorization_code
&code=d8c2afe6ecca004eb4bd7024
&redirect_uri=https://www.client.com/callback
&code_verifier=2D9RWc5iTdtejle7GTMzQ9Mg15InNmqk3GZL-Hg5Iz0
Use Postman below to demonstrate the authorization process using PKCE mode.
Reference
- https://www.rfc-editor.org/rfc/rfc6749
- https://www.rfc-editor.org/rfc/rfc7636.html
- https://oauth.net/2/pkce
- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-04
Microsoft Most Valuable Professional (MVP)
Microsoft's Most Valuable Expert is a global award granted by Microsoft to third-party technology professionals. For 28 years, technology community leaders around the world have won this award for sharing their expertise and experience in online and offline technology communities.
MVP is a team of experts who have been carefully selected. They represent the most skilled and intelligent people, and they are experts who have great enthusiasm for the community and are willing to help others. MVP is committed to helping others through speeches, forum questions and answers, creating websites, writing blogs, sharing videos, open source projects, organizing conferences, etc., and to help users in the Microsoft technology community use Microsoft technology to the greatest extent.
For more details, please visit the official website:
https://mvp.microsoft.com/zh-cn
Welcome to follow the Microsoft China MSDN subscription account for more latest releases!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。