Pourquoi JSON-RPC ?

Suite au précédent post, il m'a été demandé des précisions par rapport au protocole JSON-RPC, principalement sur l'aspect sécuritaire. Pour cela, nous allons d'abord voir un exemple d'attaque XSS dans un contexte classique : un formulaire.

Mais avant toute chose, il faut définir ce qu'est une attaque XSS. XSS, pour Cross-Site Scripting, représente une tentative de faire executer du code, malicieux de préférence, à un utilisateur depuis un site vers un autre site, sur lequel il est authentifié de préférence.

Par exemple, en vous rendant ici même sur mon blog, vous pourriez, à l'insu de votre plein gré, sans même vous en apercevoir, charger une page web d'un tout autre site, votre plateforme de blog favorite par exemple, sur l'url chargée de supprimer votre compte. Comme vous y étiez il y a 5 minutes, vous êtes toujours authentifié et donc le lien fonctionne.

Bon heureusement, dans cet exemple, l'éditeur de la plateforme de blog a bien fait les choses et un mail de confirmation est d'abord envoyé pour valider la suppression. Mais vous avez normalement compris le principe.

Passons aux travaux pratiques.

Attaque XSS par formulaire

Prenons le site victime, que nous surnommerons le site V, comme vandetta victime. Et le site méchant, que nous appellerons le site C comme chinois Cross-Site Scripted. Vous êtes authentifié sur le site V. Le site V propose un formulaire pour changer ses informations personnelles qui se soumet sur l'url http://siteV.com/user/save et qui contient les champs name et password.

Je suis sur le site V, je remplis les champs, et je clique sur OK. Une requête est alors envoyé à site V sur l'url http://siteV.com/user/save avec les nouveaux name et password.

Je ne vous apprends rien jusqu'ici. Ou alors, il faut changer de blog.

Maintenant, je suis sur site C. Celui-ci propose un formulaire prétendant créer un compte avec des champs cachés name et password dont les valeurs sont définies par site C, et l'action du formulaire pointe vers : http://siteV.com/user/save. Que va-t-il se passer quand vous allez cliquer sur OK ? Vous allez être envoyé sur site V, exactement comme si vous aviez soumis le formulaire depuis celui-ci. Site C vient de changer votre nom et votre mot de passe sur site V sans que vous ne vous en rendiez compte.

Bon en fait, là vous l'avez vu, puisque vous avez été redirigé vers site V. Cependant, il suffit d'ajouter un attribut target au formulaire pour qu'il se valide dans une iframe cachée et que vous n'y voyiez que du feu.

Heureusement, il existe des techniques pour se prémunir de tout ça : vérification du referrer, token, etc.

JSON-RPC

La particularité du JSON-RPC est que le body de la requête est un JSON. Dans une requête classique générée par un formulaire, une chaine de texte séparée par des esperluettes est envoyée. Pour une API REST, qui fonctionne exactement comme un formulaire de la section précédente, c'est ceci qui sera envoyé :

name=kikoo&password=lol

Pour du JSON-RPC, nous aurions quelquechose comme ceci :

{"jsonrpc":"2.0","method":"user.save","params":{"name":"kikoo","password":"lol"},"id":1}

C'est à dire que c'est une requête qu'il n'est pas possible de générer à partir d'un formulaire ou d'un lien. La seule possibilité de générer cette requête est d'utiliser XML-HTTP-Request, autrement dit, AJAX. Et la particularité d'AJAX, c'est que, en temps normal, il est impossible d'éxécuter une requête AJAX entre différents domaines. Site C aura beau essayer d'envoyer une requête JSON-RPC en AJAX vers site V, il n'obtiendra jamais de réponse.

Bon, j'ai précisé "en temps normal", car il est possible d'autoriser les requêtes entre différents domaine avec CORS (Cross-origin resource sharing). Voir le chapitre suivant.

Donc nous voyons ici que la seule possibilité pour les hackeurs de site C d'aller modifier nos données sur site V, est d'injecter un javascript sur l'une des pages de site V, ce qui n'est, dans la plupart des cas, pas chose facile. Une banque ou un site marchand évitera le plus possible de permettre à ses utilisateurs de sauvegarder du html/javascript. Pour les plateformes qui le permettent par nature, les CMS par exemple, elles choisiront un domaine pour la partie publique différente du backoffice.

ID

Dans le cas où nous devons exposer notre API à d'autres domaines avec CORS, il existe un moyen supplémentaire de sécuriser notre requête. C'est le paramètre id. Ce paramètre est totalement libre et c'est à l'architecte de l'API de définir son fonctionnement. Il peut être purement et simplement ignoré. Mais il peut aussi servir à identifier les requêtes en vérifiant sa validité.

Voici quelques exemples d'utilisation qui peuvent en être fait :

Identifiant de session

Le plus simple, on y passe l'identifiant de la session au lieu de se servir des cookies. On fait en sorte que cet identifiant soit le plus difficile à récupérer via du JS injecté, mais ça reste faillible.

Token généré

On génère un token à partir d'un algorithme et de plusieurs données comme l'identifiant de session, l'id utilisateur, et le datetime. Ça complique la compréhension de l'algorithme pour le hacker et ça permet d'avoir des token qui changent à chaque requête et qui expirent dans le temps, mais comme l'algo est forcément présent publiquement puisqu'il est exécuté coté client, il peut être compromis.

Token demandé au serveur

C'est la méthode la plus sécurisée, mais la plus lourde en terme de mise en place et de charge. Avant chaque requête, le client va demander un token au serveur, qui va donc être généré coté serveur avec un algorithme inaccessible au hacker. Celui-ci sera utilisé pour une requête unique. C'est la même méthode qui est utilisée pour sécuriser les formulaires classiques.

Hadrien

Hi, I'm a french Javascript Lead Developer, Web Architect from Toulouse, France. I've worked for 12 years for many projects with YUI, AngularJS, Aurelia.io and now React and React native.

Toulouse, France https://hadrien.eu