image
image
image
image
image
image

Pomme jwt

Andrea Leopardi

Si vous avez déjà eu à interagir avec les API Apple liées aux achats sur l’App Store et autres (comme les notifications du serveur de l’App Store), il est possible que vous ayez dû vérifier les JWT à un moment donné. J’avais utilisé, signé et vérifié les JWT de manière intensive avant cela, mais Apple utilise une façon un peu unique de signer ses JWT que je n’avais jamais rencontrée dans le passé. Dans ce court article, je vais montrer un peu de code autour de la vérification des signatures JWT d’Apple dans Elixir.

Photo par FLY :D sur Unsplash

Chez Veeps, nous travaillons sur certaines fonctionnalités liées à l’App Store. Il s’agit de vérifier que les notifications (webhooks) qu’Apple nous envoie sont légitimes.

Une remarque avant de commencer : j’utilise le terme JWT ici principalement par ignorance et par désir pour améliorer le classement de cet article de blog dans les moteurs de recherche ! Ce qu’Apple nous envoie, c’est un JWS (JSON Web Signature). Les JWT sont la charge utile qui est expédiée à l’intérieur d’un JWS. La RFC 7519 est celle que vous voulez consulter si vous voulez en savoir plus à ce sujet. Quoi qu’il en soit, des détails.

Jetons d’abord un coup d’œil à l’idée générale de la façon dont Apple signe ces JWT. Après cela, nous examinerons un peu de code Elixir.

Le problème avec les JWT d’Apple est qu’ils sont signés via des certificats plutôt que via des signatures symétriques ou asymétriques. Essentiellement, Apple signe la charge utile du JWT avec une clé privée. Ensuite, ils incluent la clé publique pour cela dans un certificat DER dans le JWS lui-même (le certificat « feuille »). Ce certificat est lui-même signé par un certificat intermédiaire , qui est à son tour signé via leur certificat racine . La racine Les certificats et les certificats intermédiaires sont publics et disponibles auprès de l’autorité de certification d’Apple (Apple PKI).

Techniquement

, vous n’avez pas vraiment besoin de bibliothèques Elixir/Erlang tierces pour cela, mais j’ai choisi d’en utiliser deux agiles et largement utilisées :

  • JOSE est une bibliothèque Erlang fondamentale (avec une API Elixir également) qui implémente de nombreux composants décrits dans la série de . J’ai utilisé cette bibliothèque à plusieurs reprises dans le passé pour signer et vérifier des JWT.

  • X509 est une bibliothèque Elixir qui simplifie l’utilisation des certificats X.509. Il est construit par Bram Verburg, que je considère comme la personne la plus compétente en matière de sécurité web dans les communautés Erlang et Elixir.

Allons-y.

Dans

le code que je vais montrer, je ne fais pas de gestion d’erreurs que je considérerais digne d’un code de production. Je ne fais qu’assortir des trucs avec ou dans des têtes de fonction. Cela conduit à d’horribles et s et ainsi de suite, qui sont difficiles à déboguer et ne fournissent pas vraiment de contexte sur ce qui n’a pas fonctionné. En production, je n’aurais jamais envie de ça ! Le code réel que j’ai écrit utilise le modèle /, que j’utilise pour lancer des erreurs que je renvoie à la fin de la fonction de niveau supérieur et que je renvoie sous forme de tuples.

/ est un modèle « dangereux » car il utilise des retours non locaux. Cependant, c’est un modèle que j’ai atteint plusieurs fois dans ce type de situations. L’astuce est de ne jamais laisser un terme lancé sortir d’une fonction locale, ou d’un module tout au plus. De cette façon, les utilisateurs des fonctions qui lancent n’ont pas besoin de savoir que ces fonctions utilisent / en interne.

La première chose à faire lors de la vérification de l’un de ces JWT est de jeter un coup d’œil à son en-tête. Cela n’effectue aucune vérification, mais l’en-tête contient la chaîne de certificats que nous devons vérifier la signature du JWT, donc nous devons l’examiner. JOSE fournit une fonction qui renvoie l’en-tête du JWT sous la forme d’une chaîne brute encodée en JSON :

Right. L’algorithme devrait être , alors pourquoi ne pas ajouter une vérification pour cela :

Extraction et vérification des certificats

Ensuite, nous devons examiner le dans l’en-tête. Le champ d’en-tête est documenté dans la RFC JWT (RFC 7515, sec. 4.1.6). Il s’agit d’un tableau JSON de certificats DER PKIX codés en base 64. Ce sont des acronymes complexes dont vous et moi n’avons vraiment pas à nous soucier. La RFC (et Apple) dit que vous devez valider la chaîne de certificats.

Tout d’abord, extrayons la chaîne de certificats :

maintenant, nous pouvons vérifier la chaîne de certificats. Nous allons suivre le processus que j’ai décrit ci-dessus :

  1. Nous allons vérifier que le certificat racine est le même que le certificat qu’Apple propose sur Apple PKI.
  2. Nous allons vérifier que la chaîne de certificats est valide , ce qui signifie que chaque certificat a été utilisé pour signer le suivant dans la chaîne.

Le code ci-dessous suppose que vous avez téléchargé le certificat racine Apple (en particulier). Il le lit au moment de la compilation et l’intègre dans le bytecode du module, de sorte que vous n’avez pas à livrer le fichier avec votre application de production ou votre version.

Bram m’a essentiellement tenu la main à travers ce code, alors merci, Bram ! Nous pouvons utiliser la fonction d’assistance pour vérifier la chaîne de certificats et l’exploser si elle ne semble pas correcte :

Vérification de la signature du JWS

Enfin, la dernière étape (et fondamentale) : nous devons vérifier que la clé X.509 dans le certificat feuille a été utilisée pour signer l’ensemble du JWT. JOSE fournit la fonction pour construire une structure JWK à partir d’une clé publique :

Maintenant, la partie la plus simple de tout cela : nous utilisons simplement la fonction de JOSE pour vérifier la signature dans le JWT :

Conclusion

Il n’y a pas grand-chose d’autre à dire à ce sujet. J’ai passé beaucoup de temps dessus et sur la façon de le faire dans Elixir après avoir trouvé plusieurs morceaux de code pour le faire dans d’autres langages. Par exemple, Apple fournit des « bibliothèques App Store » pour un tas de langages (jetez un coup d’œil à celui de Python). Apple fournit également une vidéo utile qui explique ce processus.

Eh bien, j’espère que cela aidera quelqu’un. Merci de votre visite !