Comment sécuriser les appels d'API REST?

91

Je développe une application Web reposante qui utilise un cadre Web populaire sur le backend, par exemple (rails, sinatra, flask, express.js). Idéalement, je souhaite développer le côté client avec Backbone.js. Comment laisser uniquement mon côté client JavaScript interagir avec ces appels d'API? Je ne veux pas que ces appels d'API soient publics et soient appelés par curlou simplement en entrant le lien sur le navigateur.

knd
la source
Tous vos appels d'API nécessitent-ils un jeton qui est transmis au client lorsque votre page est diffusée?
hajpoj
Le SDK javascript Amazon AWS utilise une URL d'objet pré-signé: - docs.aws.amazon.com/AmazonS3/latest/dev/…
rjha94

Réponses:

90

Comme premier principe, si votre API est consommée par votre client JS, vous devez supposer qu'elle est publique: un simple débogueur JS met un attaquant dans une position, où il peut envoyer une requête identique octet par octet à partir d'un outil de son choix.

Cela dit, si j'ai bien lu votre question, ce n'est pas ce que vous voulez éviter: ce que vous ne voulez vraiment pas, c'est que votre API soit consommée (régulièrement) sans que votre client JS ne soit impliqué. Voici quelques idées pour savoir comment, sinon appliquer, au moins encourager à utiliser votre client:

  • Je suis sûr que votre API a une sorte de champ d'authentification (par exemple Hash calculé sur le client). Sinon, jetez un œil à cette question SO . Assurez-vous d'utiliser un sel (ou même une clé API) qui est donné à votre client JS sur une base de session (pas codé en dur). De cette façon, un consommateur non autorisé de votre API est obligé de travailler beaucoup plus.

  • Lors du chargement du client JS, souvenez-vous de certains en-têtes HTTP (l'agent utilisateur vient à l'esprit) et de l'adresse IP et demandez une nouvelle authentification s'ils changent, en utilisant des listes noires pour les suspects habituels. Cela oblige un agresseur à refaire ses devoirs de manière plus approfondie.

  • Du côté du serveur, souvenez-vous des derniers appels d'API, et avant d'en autoriser un autre, vérifiez si la logique métier autorise le nouveau maintenant: cela empêche un attaquant de concentrer plusieurs de ses sessions en une seule session avec votre serveur: En combinaison avec les autres mesures, cela rendra un agresseur facilement détectable.

Je n'aurais peut-être pas dit cela avec la clarté nécessaire: je considère qu'il est impossible de rendre complètement impossible pour un abuseur de consommer votre service, mais vous pouvez le rendre si difficile que cela ne vaut peut-être pas la peine.

Eugen Rieck
la source
c'est une information utile, mais que se passe-t-il si je veux faire une authentification de mon api backend vers une autre application api comme un serveur séparé, pour simplifier ma question, je veux que mon back-end aka node.js envoie une demande de récupération à un autre back- serveur final qui est le mien, pour certaines raisons, cela est nécessaire, mais je veux sécuriser les appels de l'API, car il peut accéder à des données sensibles, et je ne peux pas utiliser sesions ou jwt car je ne peux pas les stocker réellement dans le navigateur.
La pyramide le
@Thepyramid Peu importe ce que fait l'appel d'API côté serveur, surtout si le côté serveur effectue un autre appel d'API de deuxième niveau. L'important est de traiter votre serveur non pas comme un proxy, mais comme une application.
Eugen Rieck
pouvez-vous expliquer plus comment faire comme application et non comme proxy
La pyramide
1
Ce que je veux dire, c'est: pour obtenir un niveau de sécurité décent, vous devez utiliser tous les outils dont dispose une application Web: des sessions, une base de données d'authentification, une logique métier. Si vous ne le faites pas et traitez simplement votre serveur comme un moyen de transmettre des demandes à un autre serveur, vous l'utilisez simplement comme proxy pour cet autre serveur et vous êtes limité par la sécurité offerte par l'autre serveur.
Eugen Rieck
1
@PirateApp Un attaquant peut facilement ignorer simplement les en-têtes CSRF. Ils ne fonctionnent que si le périphérique final est un navigateur non corrigé
Eugen Rieck
12

Vous devez implémenter une sorte de système d'authentification. Une bonne façon de gérer cela est de définir certaines variables d'en-tête attendues. Par exemple, vous pouvez avoir un appel d'API auth / login qui renvoie un jeton de session. Les appels ultérieurs à votre API s'attendront à ce qu'un jeton de session soit défini dans une variable d'en-tête HTTP avec un nom spécifique tel que «votre-jeton-api».

Alternativement, de nombreux systèmes créent des jetons d'accès ou des clés qui sont attendus (comme YouTube, Facebook ou Twitter) en utilisant une sorte de système de compte API. Dans ces cas, votre client devrait les stocker d'une manière ou d'une autre dans le client.

Ensuite, il s'agit simplement d'ajouter une vérification de la session dans votre framework REST et de lever une exception. Si possible, le code d'état (être reposé) serait une erreur 401.

gview
la source
8
Bien que rien ne les empêche de regarder les en-têtes et de les reproduire.
cdmckay
1
@cdmckay - Un jeton doit correspondre à un jeton stocké dans une session. La simple reproduction de l'en-tête entraînera une réponse "Non autorisé" s'il provient d'une session différente.
Andrei Volgin
3
Ils peuvent toujours utiliser la même session et modifier les requêtes avant qu'elles ne soient envoyées à l'API ... ou même en utilisant la console au moment de l'exécution, générer un appel avec des en-têtes / champs correspondants modifiant uniquement les parties dont vous avez besoin ...
Potter Rafed
2
@PotterRafed: Si un utilisateur accède à sa propre session valide, qui est appelée à l'aide d'une application, sans l'attaquer. Le but de l'authentification est d'empêcher l'accès aux sessions / données des autres utilisateurs .
Andrei Volgin
@AndreiVolgin ouais assez juste, mais c'est toujours une vulnérabilité
Potter Rafed
9

Il existe désormais un standard ouvert appelé "JSON Web Token",

voir https://jwt.io/ & https://en.wikipedia.org/wiki/JSON_Web_Token

JSON Web Token (JWT) est un standard ouvert basé sur JSON (RFC 7519) pour créer des jetons qui revendiquent un certain nombre de revendications. Par exemple, un serveur peut générer un jeton dont la revendication est «connecté en tant qu'administrateur» et le fournir à un client. Le client peut ensuite utiliser ce jeton pour prouver qu'il est connecté en tant qu'administrateur. Les jetons sont signés par la clé du serveur, ce qui permet au serveur de vérifier que le jeton est légitime. Les jetons sont conçus pour être compacts, sécurisés pour les URL et utilisables, en particulier dans le contexte de l'authentification unique (SSO) du navigateur Web. Les revendications JWT peuvent généralement être utilisées pour transmettre l'identité des utilisateurs authentifiés entre un fournisseur d'identité et un fournisseur de services, ou tout autre type de revendications tel que requis par les processus métier. [1] [2] Les jetons peuvent également être authentifiés et chiffrés. [3] [4]

bbozo
la source
Qu'est-ce qui empêcherait un utilisateur de copier son jeton et de l'utiliser dans toute autre réponse?
Ulad Kasach
1
@UladKasach pour être honnête, je ne les ai jamais vraiment approfondis, mais afaik qu'ils sont expirables, uniques pour votre utilisateur et cryptés par SSL (que vous pratiquez bien sûr), c'est exactement la même idée derrière oauth afaik
bbozo
3

Excusez-moi @MarkAmery et Eugene, mais c'est incorrect.

Votre application js + html (client) exécutée dans le navigateur PEUT être configurée pour exclure les appels directs non autorisés à l'API comme suit:

  1. Première étape: configurez l'API pour exiger une authentification. Le client doit d'abord s'authentifier via le serveur (ou un autre serveur de sécurité) par exemple en demandant à l'utilisateur humain de fournir le mot de passe correct.

Avant l'authentification, les appels à l'API ne sont pas acceptés.

Lors de l'authentification, un "jeton" est renvoyé.

Après l'authentification, seuls les appels d'API avec le «jeton» d'authentification seront acceptés.

Bien sûr, à ce stade, seuls les utilisateurs autorisés qui ont le mot de passe peuvent accéder à l'API, bien que s'ils sont des programmeurs déboguant l'application, ils peuvent y accéder directement à des fins de test.

  1. Deuxième étape: configurez maintenant une API de sécurité supplémentaire, qui doit être appelée dans un court laps de temps après que l'application client js + html a été initialement demandée au serveur. Ce "rappel" indiquera au serveur que le client a été téléchargé avec succès. Limitez le fonctionnement de vos appels d'API REST uniquement si le client a été demandé récemment et avec succès.

Maintenant, pour utiliser votre API, ils doivent d'abord télécharger le client et l'exécuter dans un navigateur. Ce n'est qu'après avoir reçu avec succès le rappel, puis l'entrée de l'utilisateur dans un court laps de temps, que l'API acceptera les appels.

Vous n'avez donc pas à craindre qu'il s'agisse d'un utilisateur non autorisé sans informations d'identification.

(Le titre de la question, `` Comment sécuriser les appels d'API REST '', et d'après la plupart de ce que vous dites, c'est votre principale préoccupation, et non la question littérale de COMMENT votre API est appelée, mais plutôt PAR QUI, correct? )

pashute
la source
5
Le deuxième point n'a aucun sens. Si un attaquant a besoin de charger votre application, il le fera (votre rappel est visible). Et puis attaquez.
Andrei Volgin
Le point 2 s'ajoute au point 1. L'attaquant a toujours besoin d'une authentification. Le point 2 ne fait qu'ajouter à cela la nécessité de télécharger réellement l'application html pour être autorisé. Ainsi, un appel directement aux API sans l'application (vraisemblablement accédé et téléchargé uniquement après authentification) est impossible. C'est quelque chose qui a été demandé dans cette question.
pashute
Vous pouvez simplement autoriser les demandes provenant de votre domaine uniquement.
Andrei Volgin
Cela ne limite que les appels à l'intérieur du domaine, donc maintenant les utilisateurs de l'application de navigateur javascript doivent être à l'intérieur du domaine (probablement pas quelque chose que Knd voulait) et ces utilisateurs peuvent toujours appeler l'API directement via curl.
pashute le
2
Ce que vous semblez oublier, c'est que TOUT ce que vous demandez au navigateur de votre utilisateur de faire, peut être répliqué par un attaquant - pour que le navigateur le fasse, il doit être lisible.
Eugen Rieck
1
  1. Définissez une variable SESSION sur le serveur lorsque le client charge pour la première fois votre index.html(ou backbone.jsetc.)

  2. Vérifiez cette variable côté serveur à chaque appel d'API.

PS ce n'est pas une solution "sécurité" !!! Ceci est juste pour alléger la charge sur votre serveur afin que les gens ne l'abusent pas ou ne "relient pas" votre API à partir d'autres sites Web et applications.

Alex
la source
0

Voici ce que je fais:

  1. Sécurisez l'API avec un en-tête HTTP avec des appels tels que X-APITOKEN:

  2. Utilisez des variables de session en PHP. Mettez en place un système de connexion et enregistrez le jeton utilisateur dans les variables de session.

  3. Appelez du code JS avec Ajax vers PHP et utilisez la variable de session avec curl pour appeler l'API. De cette façon, si la variable de session n'est pas définie, elle n'appellera pas et le code PHP contient le jeton d'accès à l'API.

Pavneet Singh
la source