web-dev-qa-db-fra.com

REST authentification et exposition de la clé API

J'ai lu sur REST) et il y a beaucoup de questions sur SO), ainsi que sur de nombreux autres sites et blogs. Bien que je n’aie jamais vu cette question spécifique posée ... pour une raison quelconque, je ne peux pas me permettre de penser à ce concept ...

Si je construis une API RESTful et que je souhaite la sécuriser, l'une des méthodes que j'ai vue consiste à utiliser un jeton de sécurité. Lorsque j'ai utilisé d'autres API, un jeton et un secret partagé ont du sens. Ce que je ne comprends pas, c’est que javascript (XHR/Ajax) permet de demander un service de repos, ce qui empêche quelqu'un de le renifler avec quelque chose de simple, comme FireBug (ou "view source" dans le navigateur) et copier la clé API, puis se faire passer pour cette personne en utilisant la clé et le secret?

91
tjans

api secret n'est pas explicitement passé, secret est utilisé pour générer un signe de la requête en cours, côté serveur, le serveur génère le signe suivant le même processus, si les deux signe = correspond, la demande est authentifiée avec succès - ainsi, seul le signe est transmis via la demande, pas le secret.

22
James.Xu

Nous exposons une API que les partenaires ne peuvent utiliser que sur les domaines qu'ils ont enregistrés auprès de nous. Son contenu est en partie public (mais de préférence uniquement pour figurer sur les domaines que nous connaissons), mais il est principalement privé pour nos utilisateurs. Alors:

  • Pour déterminer ce que est affiché, notre utilisateur doit être connecté avec nous, mais cela est géré séparément.

  • Pour déterminer les données sont affichées, une clé publique API est utilisée pour limiter l'accès aux domaines que nous connaissons et, surtout, pour nous assurer que les données de l'utilisateur privé sont non vulnérable à CSRF .

Cette clé d'API est en effet visible par quiconque, nous n'authentifions pas notre partenaire de toute autre manière, et nous nous n'avons pas besoin de REFERER . Pourtant, c'est sécurisé:

  1. Quand notre get-csrf-token.js?apiKey=abc123 Est demandé:

    1. Cherchez la clé abc123 dans la base de données et obtenez une liste des domaines valides pour cette clé.

    2. Recherchez le cookie de validation CSRF. S'il n'existe pas, générez une valeur aléatoire sécurisée et insérez-la dans le cookie de session n HTTP uniquement . Si le cookie existait, obtenez la valeur aléatoire existante.

    3. Créez un jeton CSRF à partir de la clé d'API et de la valeur aléatoire du cookie, et signez-le . (Plutôt que de conserver une liste de jetons sur le serveur, nous signons les valeurs. Les deux valeurs seront lisibles dans le jeton signé, c'est bien.)

    4. Définissez la réponse pour qu'elle ne soit pas mise en cache, ajoutez le cookie et renvoyez un script tel que:

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }
      

    Remarques:

    • Ce qui précède n’empêche pas un script côté serveur de simuler une demande, mais garantit uniquement que le domaine correspond si est demandé par un navigateur.

    • Le même règle d'origine pour JavaScript garantit qu'un navigateur ne peut pas utiliser XHR (Ajax) pour charger puis inspecter la source JavaScript. Au lieu de cela, un navigateur classique ne peut le charger qu’en utilisant <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123"> (ou un équivalent dynamique) et exécutera ensuite le code. Bien sûr, votre serveur devrait ne pas prendre en charge partage de ressources d'origine croisée ni JSONP pour le code JavaScript généré.

    • Un script de navigateur peut changer la valeur de document.domain avant de charger le script ci-dessus. Mais la même politique d'origine ne permet de raccourcir le domaine qu'en en supprimant les préfixes , comme en réécrivant subdomain.example.com pour juste example.com, ou myblog.wordpress.com à wordpress.com, ou même dans certains navigateurs bbc.co.uk à co.uk .

    • Si le fichier JavaScript est récupéré à l'aide d'un script côté serveur, le serveur recevra également le cookie. Toutefois, un serveur tiers ne peut pas obliger le navigateur d’un utilisateur à associer ce cookie à notre domaine. Par conséquent, un jeton CSRF et un cookie de validation, récupérés à l'aide d'un script côté serveur, ne peuvent être utilisés que par les appels ultérieurs côté serveur, et non dans un navigateur. Cependant, ces appels côté serveur n'incluent jamais le cookie de l'utilisateur et ne peuvent donc que récupérer des données publiques. Il s’agit des mêmes données qu’un script côté serveur pourrait extraire directement du site Web du partenaire.

  2. Lorsqu'un utilisateur se connecte, définissez un cookie utilisateur de la manière qui vous convient. (Il est possible que l'utilisateur se soit déjà connecté avant la demande de JavaScript.)

  3. Toutes les demandes d'API ultérieures adressées au serveur (y compris les demandes GET et JSONP) doivent inclure le jeton CSRF, le cookie de validation CSRF et (s'il est connecté) le cookie de l'utilisateur. Le serveur peut maintenant déterminer si la demande doit être approuvée:

    1. La présence d'un jeton CSRF valide garantit que le code JavaScript a été chargé à partir du domaine attendu, si chargé par un navigateur.

    2. La présence du jeton CSRF sans le cookie de validation indique une falsification.

    3. La présence à la fois du jeton CSRF et du cookie de validation CSRF ne garantit rien: il peut s'agir d'une demande falsifiée côté serveur ou d'une demande valide émanant d'un navigateur. (Il ne peut s'agir d'une requête d'un navigateur émanant d'un domaine non pris en charge.)

    4. La présence du cookie de l'utilisateur garantit que l'utilisateur est connecté, mais ne garantit pas que l'utilisateur est membre du partenaire donné, ni que l'utilisateur visualise le site Web approprié.

    5. La présence du cookie utilisateur sans le cookie de validation CSRF indique une falsification.

    6. La présence du cookie de l'utilisateur garantit que la demande en cours est effectuée via un navigateur. (En supposant qu'un utilisateur ne saisisse pas ses informations d'identification sur un site Web inconnu, et en supposant que nous ne nous soucions pas que les utilisateurs utilisent leurs propres informations d'identification pour faire une requête côté serveur.) Si nous aussi ont le cookie de validation CSRF, puis ce cookie de validation CSRF a également été reçu à l'aide d'un navigateur. Ensuite, si nous aussi avons un jeton CSRF avec une signature valide, et le nombre aléatoire dans le cookie de validation CSRF correspond à celui de ce jeton CSRF; le code JavaScript correspondant à ce jeton a également été reçu lors de la même demande précédente au cours de laquelle le cookie CSRF a été défini, utilisant donc également un navigateur. Cela implique également que le code JavaScript ci-dessus a été exécuté avant la définition du jeton et qu'à ce moment-là, le domaine était valide pour la clé d'API donnée.

      Donc: le serveur peut maintenant utiliser en toute sécurité la clé API du jeton signé.

    7. Si, à un moment quelconque, le serveur n'approuve pas la demande, un message 403 Forbidden est renvoyé. Le widget peut répondre à cela en affichant un avertissement à l'utilisateur.

Il n'est pas nécessaire de signer le cookie de validation CSRF, car nous le comparons au jeton CSRF signé. Ne pas signer le cookie raccourcit chaque requête HTTP et la validation du serveur un peu plus rapidement.

Le jeton CSRF généré est valide indéfiniment, mais uniquement en combinaison avec le cookie de validation, donc jusqu'à la fermeture du navigateur.

Nous pourrions limiter la durée de vie de la signature du jeton. Nous pourrions supprimer le cookie de validation CSRF lorsque l'utilisateur se déconnecte pour respecter recommandation OWASP . Et pour ne pas partager le nombre aléatoire par utilisateur entre plusieurs partenaires, vous pouvez ajouter la clé API au nom du cookie. Mais même dans ce cas, il est difficile d’actualiser le cookie de validation CSRF lorsqu’un nouveau jeton est demandé, car les utilisateurs peuvent naviguer sur le même site dans plusieurs fenêtres et partager un seul cookie (qui, lors de l’actualisation, serait mis à jour dans toutes les fenêtres. Le jeton JavaScript dans les autres fenêtres ne correspond plus à ce cookie unique).

Pour ceux qui utilisent OAuth, voir aussi OAuth et les widgets côté client , d'où j'ai eu l'idée de JavaScript. Pour côté serveur , utiliser l'API, dans laquelle nous ne pouvons pas compter sur le code JavaScript pour limiter le domaine, nous utilisons des clés secrètes au lieu du public. Clés API.

59
Arjan

Cette question a une réponse acceptée mais juste pour clarifier, l'authentification secrète partagée fonctionne comme ceci:

  1. Le client a une clé publique, cela peut être partagé avec n'importe qui, peu importe, vous pouvez donc l'intégrer en javascript. Ceci est utilisé pour identifier l'utilisateur sur le serveur.
  2. Le serveur a une clé secrète et ce secret DOIT être protégé. Par conséquent, l'authentification par clé partagée nécessite que vous puissiez protéger votre clé secrète. Ainsi, un client javascript public qui se connecte directement à un autre service n'est pas possible car vous avez besoin d'un intermédiaire de serveur pour protéger le secret.
  3. Le serveur signe la demande en utilisant un algorithme incluant la clé secrète (la clé secrète est un peu comme un sel) et, de préférence, un horodatage envoie ensuite la demande au service. L'horodatage est destiné à empêcher les attaques de "rejeu". La signature d'une requête n'est valide que pendant environ n secondes. Vous pouvez vérifier cela sur le serveur en obtenant l'en-tête d'horodatage qui devrait contenir la valeur de l'horodatage incluse dans la signature. Si cet horodatage a expiré, la demande échoue.
  4. Le service obtient la demande qui contient non seulement la signature, mais également tous les champs signés en texte brut.
  5. Le service signe ensuite la demande de la même manière à l'aide de la clé secrète partagée et compare les signatures.
9
chris

Je suppose que vous voulez parler de la clé de session et non de la clé API. Ce problème est hérité du protocole http et est appelé détournement de session . La "solution de contournement" normale consiste, comme sur tout site Web, à passer à https.

Pour exécuter le service REST sécurisé), vous devez activer https et probablement l’authentification du client. Mais, après tout, cela dépasse l’idée REST.. REST ne parle jamais de sécurité.

1
PeterMmm

Ce que vous voulez faire côté serveur, c'est générer un identifiant de session expirant qui est renvoyé au client lors de la connexion ou de l'inscription. Le client peut ensuite utiliser cet identifiant de session comme secret partagé pour signer les demandes suivantes.

L'identifiant de session n'est passé qu'une fois et cela DOIT être sur SSL.

Voir exemple ici

Utilisez un nonce et un horodatage lors de la signature de la demande pour empêcher le détournement de session.

1
Iain Porter

Je vais essayer de répondre à la question dans son contexte d'origine. Donc la question est "Est-ce que la clé secrète (API) peut être placée en JavaScript?.

À mon avis, il est très dangereux car cela va à l’encontre du but de l’authentification entre les systèmes. Étant donné que la clé sera exposée à l'utilisateur, l'utilisateur peut récupérer des informations pour lesquelles il/elle n'est pas autorisé. Parce que, dans un repos typique, l’authentification de la communication n’est basée que sur la clé d’API.

Une solution à mon avis est que l'appel JavaScript passe essentiellement la demande à un composant de serveur interne qui est responsable de faire un appel de repos. Le composant de serveur interne, disons qu'un Servlet lira la clé d'API à partir d'une source sécurisée telle qu'un système de fichiers basé sur une autorisation, l'insérera dans l'en-tête HTTP et effectuera l'appel externe restant.

J'espère que ça aide.

1
MG Developer