PKCE (prononcer « pixy » ) est un acronyme pour "Proof Key for Code Exchange" (clé de preuve pour l’échange de code).
Voir RFC 7636 : Flux de code d’autorisation avec clé de vérification pour l’échange de code (Proof of Key for Code Exchange, PKCE)
Utilité de PKCE pour les applications mobiles / natives
Lors de l’authentification, les applications mobiles / natives ( sans back-end ) peuvent utiliser le flux de code d’autorisation, mais elles requièrent une sécurité supplémentaire, car elles ne peuvent stocker en toute sécurité le secret du client qui pourrait être révélé par reverse-engineering (le secret client est lié à l’application et est identique pour tous les utilisateurs et appareils).
Les applications sans back-end peuvent utiliser un schéma d’URL personnalisé pour capturer les redirections (par exemple, MyApp ://). Ceci peut être détourné par une application malveillante, résidant sur le même user-agent, pour recevoir un code d’autorisation.
Pour pallier ce problème, OAuth 2.0 et OpenID Connect fournissent un complément au flux de code d’autorisation utilisant une clé de vérification pour l’échange de code (PKCE).
Flux de code d’autorisation + PKCE
Une application cliente mettant en œuvre le flux de code d’autorisation avec PKCE doit être du type "publique" c’est à dire être enregistrée sur le serveur avec un secret null.
L’application cliente publique crée une valeur de chaîne aléatoire (unique et ne contenant pas d’information), le code_verifier. Lorsque l’application cliente initie la demande de code d’autorisation, au lieu du secret, elle envoie au contrôleur Authorize le hash du code_verifier sous le paramètre code_challenge.
Une fois que l’utilisateur s’est authentifié et que le code d’autorisation est renvoyé à l’application cliente, celle-ci demande les jetons en échange du code d’autorisation, en incluant le paramètre code_verifier.
Si les codes correspondent, l’authentification est terminée et un access_token est renvoyé.
Création du code_challenge
Dans la demande du code d’autorisation, l’application doit transmettre le code_challenge et la méthode de codage "code_challenge_method" [1].
Partant d’une chaîne aléatoire ’code_verifier’, on obtient le code_challenge en appliquant un hachage SHA256 et un codage URL base64. Il faut alors passer code_challenge_method = ’S256’
PHP
- // Prepare PKCE code_challenge
- $code_verifier = substr( str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEF"), 0, 50);
- $code_challenge = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_');
- $_SESSION['code_verifier'] = encrypt($code_verifier);
- ...
Javascript
- function base64URLEncode(str) {
- return str.toString('base64')
- .replace(/\+/g, '-')
- .replace(/\//g, '_')
- .replace(/=/g, '');
- }
- var code_verifier = crypto.randomBytes(50);
- var code_challenge = base64URLEncode(code_verifier );
- ...
Constante de configuration ENFORCE_PKCE
Si la constante de configuration ENFORCE_PKCE est fixée à true, tous les clients doivent appliquer PKCE.
Si false, PKCE est appliqué seulement aux clients interrogeant Authorize avec le paramètre code_challenge.
Cette constante est fixée à False par défaut.
Erreur ’missing_code_challenge’
Si la constante de configuration ENFORCE_PKCE est fixée à true ou si le client interroge Authorize avec le paramètre code_challenge, et que le client ne fournit pas le code challenge ainsi que la méthode de codage dans la demande de code d’autorisation, le contrôleur Authorise répond avec un code 400 et le message ’missing_code_challenge’, ’This application requires you provide a PKCE code challenge’.
Appel au contrôleur Token
Lorsque l’application cliente appelle le contrôleur Token pour obtenir les jetons, elle passe le paramètre code_verifier et le client ID dans le corps de la requête POST ( au lieu de passer le client ID et le secret dans le header par la méthode d’authentification HTTP Basic ).
Exemple en PHP :
- $code_verifier = decrypt(@$_SESSION['code_verifier']);
- 'client_id' => $client_id,
- 'grant_type' => 'authorization_code',
- 'code' => $code,
- 'code_verifier' => $code_verifier,
- );
- //curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
- ...
Erreur ’code_verifier_missing’
Si le client a interrogé Authorize avec un code_challenge (quelque soit la valeur de la constante de configuration ENFORCE_PKCE), et que le client ne fournit pas le paramètre ’code_verifier’ dans la demande de jeton, le contrôleur Token répond avec un code 400 et le message ’code_verifier_missing’, "The PKCE code verifier parameter is required’.
Discussion sur la sécurité de PKCE
Tout d’abord rappelons que PKCE ne remplace pas le secret de l’application, et n’apporte donc aucune réponse à la problématique de l’identification de l’application cliente.
Le flux de code d’autorisation amélioré par PKCE introduit un code aléatoire "code_verifier", obscur (ne contenant pas d’information), créé par l’application appelante et pouvant être vérifié par le serveur d’autorisation. En outre, l’application appelante crée une valeur transformée du vérificateur de code appelée "code_challenge" et envoie cette valeur via HTTPS pour récupérer un code d’autorisation. De cette manière, un attaquant malveillant ne peut qu’intercepter le code d’autorisation et ne peut pas l’échanger contre un jeton sans le vérificateur de code.
Cependant, après avoir créé le code verifier dans la première étape du flux de code d’autorisation, l’application cliente doit le stocker quelque part pour le fournir à la deuxième étape, lors de la demande de jeton. Même si cela ne dure qu’un temps très court (de l’ordre de la seconde), le code verifier est exposé à un malware dont force est de constater qu’il a tout ce qu’il faut pour tenter d’obtenir un jeton à la place de l’application légitime. En principe, l’application légitime devrait avoir demandé le jeton immédiatement, probablement avant le malware qui butera sur l’interdiction d’utiliser deux fois le même code verifier. Sauf si le hacker est capable de jouer sur les priorités d’exécution ? Même si cela paraît difficile ou improbable, il y a peut-être là une possibilité d’exploit.