Sécurité et vérification
Vous devez respecter les règles de sécurité décrites par OAuth et OpenID Connect.
Faille dans l'autorisation par code
Il existe une faille dans la cinématique Authorization code flow qui permet à une personne malveillante de récupérer les identifiants d'un utilisateur. Cette faille a affecté des sites web connus (Pinterest, SoundCloud, Digg...) qui n'ont pas correctement implémenté cette cinématique.
Il existe un moyen d'empêcher cela grâce aux paramètres state
dans OAuth 2.0 et nonce
dans OpenID Connect. Si votre application envoie ces paramètres lors de la demande d'authentification de l'utilisateur, ils seront retournés par Pôle Emploi Access Management et vous pourrez les comparer.
Vous devez obligatoirement utiliser ces deux paramètres :
Paramètre | Description |
---|---|
State | Retourné en réponse à la demande d'authentification de l'utilisateur |
Nonce |
Retourné en réponse à la demande de génération d'un access token |
Vérification des tokens
Définition de l'id token
Un id token est un jeton au format JWT (JSON Web Token) qui contient des informations sur l'authentification d'un utilisateur final. Un jeton JWT est une façon de représenter des claims pour les transférer entre deux parties dans un format compact et compatible URI. Un claim est simplement une paire clé/valeur contenant des informations sur un utilisateur ou sur le contexte d'authentification.
L'id token est composé de trois parties (entête, claims et signature) séparées par des . (point) et encodées en base64url :
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
- L'entête est un objet JSON doté des propriétés définies par les normes JWS et JWE suivant l'encryptage ou la signature du jeton. Il contient des métadonnées ainsi que le nom des algorithmes et clés nécessaires à ces algorithmes.
- Les claims sont un ensemble de clés/valeurs dont les noms sont normalisés dans la norme OpenID Connect. Ils sont sérialisés en JSON.
- La signature correspond à la concaténation de l'entête et des claims chiffrée avec la clé privée. Elle est utilisée pour vérifier que l’expéditeur du jeton JWT est bien celui qu’il prétend être et de veiller à ce que le message n’ait pas été modifié en cours de route.
Exemple d'id token :
"id_token":"eyAidHlwIjogIkpXVCIsICJhbGciOiAiSFMyNTYiIH0.eyAidG9rZW5OY
W1lIjogImlkX3Rva2VuIiwgImF6cCI6ICJlbXBsb2lzdG9yZS1lbXBsb3lldXItYWdlbnQ
iLCAic3ViIjogIjIxMjM3Nzc1MjEiLCAiYXRfaGFzaCI6ICJIOVFyVnYwcTl5QjRsdzV3Z
i1IUDdnIiwgImlzcyI6ICJodHRwczovL3NvMDE3LXBvYW0tYWphczc0My5zaWkyNC5wb2x
lLWVtcGxvaS5pbnRyYToxNzQzMC9jb25uZXhpb24vb2F1dGgyL2luZGl2aWR1IiwgIm9yZ
y5mb3JnZXJvY2sub3BlbmlkY29ubmVjdC5vcHMiOiAiZTA2MDQ2M2YtYzEzNi00N2M3LTg
1MmUtYmM0MDIxMTc0ZGRmIiwgImlhdCI6IDE0ODU4NjA5OTQsICJhdXRoX3RpbWUiOiAxN
Dg1ODU5MjI0LCAiZXhwIjogMTQ4NTg2NDU5NCwgInRva2VuVHlwZSI6ICJKV1RUb2tlbiI
sICJhdWRpdFRyYWNraW5nSWQiOiAiZWE5MTQ3NWQtZDRhZS00MDQxLWE4NjUtMjI1YjZiY
TEzYTdiLTc2NTgiLCAibm9uY2UiOiAiZDFjN2M5OWM2ZTJlN2IzMTFmNTFkZDlkMTkxNjF
hNTgzMjYyNWZiMjFmMzUxMzFmYmE2ZGE2MjUxM2YwYzA5OSIsICJyZWFsbSI6ICIvaW5ka
XZpZHUiLCAiYXVkIjogImVtcGxvaXN0b3JlLWVtcGxveWV1ci1hZ2VudCIsICJjX2hhc2g
iOiAidmlqdnZVLXJQcHpXUkdtMVRZbm1GZyIgfQ._a2BNpM7dLXlWZUe9oJnbvalpvrUjo
VFnznV2OvgoZc"
Exemple d'id token décodé :
Partie | Valeur brute | Valeur base64 décodée |
---|---|---|
Entête (1) | eyAidHlwIjogIkpXVCIsICJhb GciOiAiSFMyNTYiIH0 |
{ "typ": "JWT", "alg": "HS256" } |
Claims (2) | eyAidG9rZW5OYW1lIjogImlkX 3Rva2VuIiwgImF6cCI6ICJlbX Bsb2lzdG9yZS1lbXBsb3lldXI tYWdlbnQiLCAic3ViIjogIjIx MjM3Nzc1MjEiLCAiYXRfaGFza CI6ICJIOVFyVnYwcTl5QjRsdz V3Zi1IUDdnIiwgImlzcyI6ICJ odHRwczovL3NvMDE3LXBvYW0t YWphczc0My5zaWkyNC5wb2xlL WVtcGxvaS5pbnRyYToxNzQzMC 9jb25uZXhpb24vb2F1dGgyL2l uZGl2aWR1IiwgIm9yZy5mb3Jn ZXJvY2sub3BlbmlkY29ubmVjd C5vcHMiOiAiZTA2MDQ2M2YtYz EzNi00N2M3LTg1MmUtYmM0MDI xMTc0ZGRmIiwgImlhdCI6IDE0 ODU4NjA5OTQsICJhdXRoX3Rpb WUiOiAxNDg1ODU5MjI0LCAiZX hwIjogMTQ4NTg2NDU5NCwgInR va2VuVHlwZSI6ICJKV1RUb2tl biIsICJhdWRpdFRyYWNraW5nS WQiOiAiZWE5MTQ3NWQtZDRhZS 00MDQxLWE4NjUtMjI1YjZiYTE zYTdiLTc2NTgiLCAibm9uY2Ui OiAiZDFjN2M5OWM2ZTJlN2IzM TFmNTFkZDlkMTkxNjFhNTgzMj YyNWZiMjFmMzUxMzFmYmE2ZGE 2MjUxM2YwYzA5OSIsICJyZWFs bSI6ICIvaW5kaXZpZHUiLCAiY XVkIjogImVtcGxvaXN0b3JlLW VtcGxveWV1ci1hZ2VudCIsICJ jX2hhc2giOiAidmlqdnZVLXJQ cHpXUkdtMVRZbm1GZyIgfQ |
{ "tokenName": "id_token", "tokenType": "JWTToken", "azp": "[identifiant client]", "aud": "[identifiant client]", "iss": "https://authentification-candidat.pole-emploi.fr:443/connexion/oauth2/realms/root/realms/individu", "realm": "/individu", "nonce": "d1c7c99c6e2e7b311 f51dd9d19161a5832625fb21f3 5131fba6da62513f0c099", "iat": 1485860994, "exp": 1485864594, "auth_time": 1485859224, "c_hash": "vijvvU-rPpzWRGm1TYnmFg", "at_hash": "H9QrVv0q9yB4lw5wf-HP7g", "sub": "2123777521", "auditTrackingId": "ea91475d-d4ae-4041-a865-225b6ba13a7b-7658", "org.forgerock.openidconnect.ops": "e060463f-c136-47c7-852e-bc4021174ddf" } |
Signature (3) | _a2BNpM7dLXlWZUe9oJnbvalp vrUjoVFnznV2OvgoZc |
Sans objet |
Exemple de claims :
Clé | Valeur |
---|---|
tokenName | Nom du token |
tokenType | Type du token |
azp | Votre identifiant client |
aud | Votre identifiant client |
iss | URL de Pôle Emploi Access Management (Voir tableau ci-dessous) |
realm | Royaume de la population d'utilisateurs |
nonce | Identique à celui fourni lors de l'appel du endpoint d'authentification |
iat | Date de génération de l’id token |
exp | Date d’expiration de l’id token |
auth_time | Date d'authentification de l'individu |
c_hash | Sans objet |
at_hash | Permet la vérification de l'access token |
sub | Identifiant unique de l'individu |
auditTrackingId | Sans objet |
org.forgerock.openidconnect.ops | Sans objet |
Valeurs de l'ISS à contrôler
Population d'utilisateurs | URL |
---|---|
Demandeurs d'emploi et candidats |
https://authentification-candidat.pole-emploi.fr:443/connexion/oauth2/realms/root/realms/individu |
Entreprises et recruteurs |
https://entreprise.pole-emploi.fr:443/connexion/oauth2/realms/root/realms/employeur |
Vérification de l'id token
La vérification se fait en suivant ces étapes :
- Si l'id token est chiffré, le déchiffrer
- Vérifier que l'issuer présent dans le claim
iss
est bien l'URL de Pôle Emploi Access Management - Vérifier que votre identifiant client est bien présent dans le claim
aud
- Si un claim
azp
est présent, vérifier que sa valeur correspond bien à votre identifiant client - (Facultatif) Vérifier la validité de la signature électronique de l'id token en vérifiant que la date courante est :
- antérieure à la date d'expiration de l'id token présent dans le claim
exp
- proche (≤ 25 secondes) de la date de génération de l'id token présent dans le claim
iat
et de la date d'authentification de l'utilisateur présent dans le claimauth_time
(ceci implique une synchronisation de l'horloge de votre application sur l'horloge atomique)
- antérieure à la date d'expiration de l'id token présent dans le claim
- Vérifier que le claim
realm
est identique à celui positionné par votre application lors de la requête de demande d'authentification de l'utilisateur - Vérifier que le claim
nonce
est identique à celui généré par votre application lors de la requête de demande d'authentification de l'utilisateur (paramètre permettant de s'assurer de la provenance des échanges : anti-faille CSRF)
Afin de vérifier que l'id token a bien été délivré pour votre application (et qu'il n'y a pas eu d'attaque Man in the middle), celle-ci peut vérifier la signature du jeton ayant pour format :
signature = sign('<base64-encoded header>.<base64-encoded claims>')
La vérification de la signature dépend de l'algorithme utilisé. Si votre application souhaite vérifier et valider l'id token qu'elle reçoit de Pôle Emploi Access Management, elle doit récupérer la clé publique de signature dans le détail du endpoint jwk_uri.
Vérification de l'access token
La vérification se fait en suivant ces étapes :
- Faire un hash SHA256 de l'access token et ne garder que les 128 bits de gauche sur la valeur obtenue (32 octets)
- Convertir la valeur hexadécimale des 128 bits de gauche en base64
- Supprimer les éventuels = (égal) ou == obtenus en fin de chaine
- Remplacer les + (plus) par des – (moins) et les / (slash) par des _ (underline)
- Comparer la valeur obtenue au claim
at_hash
, les valeurs devant être identiques
Exemple :
- Avec l'acces token 8eb5020b-0b84-41f3-8174-6f7523805bf3, le hash obtenu est 1fd42b56fd2af72078970e707fe1cfee
- En base64, on obtient H9QrVv0q9yB4lw5wf+HP7g==
- Après suppression des caractères indiqués, on obtient H9QrVv0q9yB4lw5wf+HP7g
- Après remplacement des caractères indiqués, on obtient H9QrVv0q9yB4lw5wf-HP7g
Vous pouvez aussi remplacer les étapes 2 à 4 en convertissant directement la valeur hexadécimale des 128 bits de gauche en base64url.
La RFC 4648 prévoit une alternative pour un encodage compatible avec les noms de fichiers et les URI. En effet, les caractères 62 (plus) et 63 (slash) peuvent poser problème avec certains systèmes de fichiers et dans les URI. La solution consiste alors à remplacer ces caractères respectivement par un - (moins) et un _ (underline). Le caractère de complément reste le = (égal) mais il peut être ignoré.
Ce système est par exemple utilisé pour la réduction d'URL :
Valeur Codage Valeur Codage Valeur Codage Valeur Codage
0 000000 A 17 010001 R 34 100010 i 51 110011 z
1 000001 B 18 010010 S 35 100011 j 52 110100 0
2 000010 C 19 010011 T 36 100100 k 53 110101 1
3 000011 D 20 010100 U 37 100101 l 54 110110 2
4 000100 E 21 010101 V 38 100110 m 55 110111 3
5 000101 F 22 010110 W 39 100111 n 56 111000 4
6 000110 G 23 010111 X 40 101000 o 57 111001 5
7 000111 H 24 011000 Y 41 101001 p 58 111010 6
8 001000 I 25 011001 Z 42 101010 q 59 111011 7
9 001001 J 26 011010 a 43 101011 r 60 111100 8
10 001010 K 27 011011 b 44 101100 s 61 111101 9
11 001011 L 28 011100 c 45 101101 t 62 111110 - (moins)
12 001100 M 29 011101 d 46 101110 u 63 111111 _ (souligné)
13 001101 N 30 011110 e 47 101111 v
14 001110 O 31 011111 f 48 110000 w (complément) =
15 001111 P 32 100000 g 49 110001 x
16 010000 Q 33 100001 h 50 110010 y
Dans cette version d'encodage, les / (slash) sont remplacés par des _ (underline) et les + (plus) par des - (moins).