web-dev-qa-db-fra.com

Dogfooding notre propre API à taux limité

Présentation:

Mon entreprise a développé une API à taux limité. Notre objectif est double:

  • R: Créez un écosystème de développeurs solide autour de notre produit.
  • B: Démontrez la puissance de notre API en l'utilisant pour piloter notre propre application.

Clarification: pourquoi limiter le taux?

Nous évaluons la limite de notre API, car nous la vendons en complément de notre produit. L'accès anonyme à notre API a un seuil très bas pour les appels API par heure, alors que nos clients payants sont autorisés à plus de 1000 appels par heure ou plus.

Le problème:

Notre API à taux limité est idéale pour l'écosystème du développeur, mais pour que nous puissions le nourrir, nous ne pouvons pas le limiter à la même limite de taux. La partie frontale de notre API est entièrement JavaScript, ce qui permet de faire des appels Ajax directs à l'API.

La question est donc:

Comment sécuriser une API pour que la limitation de débit puisse être supprimée alors que dans le processus de suppression d'une telle limitation de débit ne peut pas être facilement usurpée?

Solutions explorées (et pourquoi elles n'ont pas fonctionné)

  1. Vérifiez le référent par rapport à l'en-tête Host. - Défaut car le référent est facilement truqué.

  2. Utilisez un HMAC pour créer une signature basée sur la demande et un secret partagé, puis vérifiez la demande sur le serveur. - Défaut car le secret et l'algorithme seraient facilement déterminés en regardant dans le JavaScript frontal.

  3. Proxy la demande et signer la demande dans le proxy - Toujours imparfait, car le proxy lui-même expose l'API.

La question:

Je me tourne vers les esprits brillants de Stack Overflow pour présenter des solutions alternatives. comment résoudrais-tu ce problème?

116
Jason Waldrip

Étant donné que votre propre client JavaScript accède directement à l'API, tout le monde pourra voir ce qu'il fait et l'imiter, y compris en utilisant la même clé d'API. Vous pouvez essayer de le rendre plus difficile, comme en obscurcissant votre code ou en mettant divers obstacles sur le chemin, mais vous et la personne que vous essayez de restreindre ont fondamentalement le même accès. Au lieu d'essayer de créer une différence de privilèges, vous devrez construire un système où il est tout à fait OK que le client non officiel utilise tous les accès dans sa portée, mais le système est organisé de manière à ce que l'utilisation officielle sur tous les clients soit plus grand.

Cela se fait souvent avec des jetons d'accès par utilisateur, par opposition à un jeton pour l'application entière. La limite de chaque jeton doit être suffisante pour une utilisation typique de votre API, mais restrictive pour quelqu'un qui essaie d'en abuser. Par exemple, 100 appels par minute peuvent être plus que suffisants pour prendre en charge la navigation classique, mais si je veux vous gratter, je ne peux pas le faire efficacement avec ce budget.

Il y aura toujours une course aux armements - je peux contourner la limite en créant de nombreux comptes d'utilisateurs de robots. Cependant, cela est un problème assez résolu si vous ajoutez simplement un captcha à votre flux d'inscription, à un coût minime pour le vrai humain. Lorsque vous entrez dans ces scénarios, tout n'est qu'un compromis entre commodité et restriction. Vous ne trouverez jamais quelque chose de totalement à l'épreuve des balles, alors concentrez-vous sur le rendre assez bon et attendez que quelqu'un vous exploite pour savoir où étaient les trous.

93
Kristján

Si cela vous pose un problème, cela causera un problème à votre écosystème putatif de développeurs (par exemple, lorsqu'ils essaient de développer une interface utilisateur alternative). Si vous mangez vraiment votre propre nourriture pour chien, faites fonctionner l'API (et la limitation de taux) pour votre application. Voici quelques suggestions:

  • Ne notez pas la limite par adresse IP. Au contraire, la limite de débit par quelque chose associé à l'utilisateur, par exemple leur ID utilisateur. Appliquez la limite de débit au stade de l'authentification.

  • Concevez votre API de sorte que les utilisateurs n'aient pas besoin de l'appeler en continu (par exemple, passez un appel de liste qui renvoie de nombreux résultats, plutôt qu'un appel répété qui renvoie un élément à chaque fois)

  • Concevez votre application Web avec les mêmes contraintes que vous attendez de votre écosystème de développeurs, c'est-à-dire assurez-vous que vous pouvez la concevoir dans des taux de limitation raisonnables.

  • Assurez-vous que votre back-end est évolutif (horizontalement de préférence) afin que vous n'ayez pas besoin d'imposer une limitation à des niveaux si bas que cela cause un problème à une interface utilisateur.

  • Assurez-vous que votre étranglement a la capacité de faire face aux éclats, ainsi que de limiter les abus à long terme.

  • Assurez-vous que votre limitation effectue des actions sensibles adaptées à l'abus que vous cherchez à supprimer. Par exemple, envisagez de faire la queue ou de retarder les abuseurs légers plutôt que de refuser la connexion. La plupart des frontaux Web n'ouvriront que quatre connexions simultanées à la fois. Si vous retardez une tentative d'ouverture d'un cinquième, vous ne toucherez que le cas où ils utilisent une CLI en même temps que le client Web (ou deux clients Web). Si vous retardez le n-ième appel d'API sans interruption plutôt que de l'échouer, l'utilisateur final verra les choses ralentir plutôt que casser. Si vous combinez cela avec uniquement la mise en file d'attente des appels N API, vous ne frapperez que les personnes qui parallélisent un grand nombre d'appels API, ce qui n'est probablement pas le comportement que vous souhaitez - par exemple 100 appels API simultanés, puis un écart d'une heure est normalement bien pire que 100 appels API séquentiels sur une heure.

Cela n'a-t-il pas répondu à votre question? Eh bien, si vous avez vraiment besoin pour faire ce que vous demandez, limitez le taux au stade de l'authentification et appliquez un taux différent en fonction du groupe auquel votre utilisateur appartient. Si vous utilisez un seul ensemble d'informations d'identification (utilisé par vos développeurs et votre équipe d'AQ), vous obtenez une limite de taux plus élevée. Mais vous pouvez immédiatement voir pourquoi cela vous mènera inévitablement à votre écosystème en voyant des problèmes que votre équipe de développement et d'assurance qualité ne voient pas.

33
abligh

Achetez votre produit. Devenez un client payant de vous-même.

"L'accès anonyme à notre API a un seuil très bas pour les appels API par heure, alors que nos clients payants sont autorisés à plus de 1000 appels par heure ou plus."

Cela permet également de tester le système du point de vue du client.

11
jkdev

Malheureusement, il n'y a pas de solution parfaite à cela.

L'approche générale consiste généralement à fournir un moyen surpé pour les clients de s'identifier (par exemple, un identifiant, une version et une clé API - par exemple), pour que les clients enregistrent des informations sur eux-mêmes qui peuvent être utilisées pour limiter l'accès (par exemple, le client est un serveur dans une plage d'adresses IP donnée, donc n'autorisez que les appelants dans cette plage; par exemple, le client est JavaScript, mais livré uniquement à une catégorie spécifique de navigateur, donc autorisez uniquement l'accès aux requêtes HTTP qui spécifient certains des chaînes d'agent utilisateur; etc.), puis d'utiliser l'apprentissage automatique/la reconnaissance de modèle pour détecter une utilisation anormale qui est probablement un client falsifié, puis pour rejeter le trafic de ces clients falsifiés (ou confirmer avec les clients que ces utilisations ne proviennent en fait pas du client légitime, remplacer leurs informations d'identification usurpées, puis interdire tout trafic supplémentaire à l'aide des anciennes informations d'identification usurpées).

Vous pouvez le rendre un peu plus difficile à usurper en utilisant plusieurs couches de clés. Par exemple, vous donnez des informations d'identification à durée de vie plus longue qui résident sur un serveur (et qui ne peuvent être utilisées que dans un ensemble limité de plages d'adresses IP) pour effectuer un appel d'API qui enregistre des informations sur le client (par exemple, l'agent utilisateur) et renvoie une clé côté client de courte durée qui est syndiquée en JavaScript pour une utilisation sur le client pour les demandes d'API côté client. Cela aussi est imparfait (un usurpateur peut émettre le même appel de serveur pour obtenir les informations d'identification), mais ce sera plus difficile si la clé d'API retournée est incluse dans JavaScript ou HTML obscurci (et changeant fréquemment) (ce qui rendrait la tâche difficile). extraire de manière fiable de la réponse). Cela permet également de détecter plus facilement l'usurpation d'identité; la clé côté client est désormais liée à un client particulier (par exemple, un agent utilisateur spécifique, peut-être même un fichier cookie spécifique), ce qui facilite la détection de la réutilisation dans un autre client et l'expiration limite également la durée pendant laquelle la clé usurpée peut être réutilisée.

9

En supposant que l'application en question doit être ouverte au public, vous n'avez pas beaucoup le choix:

Choisissez une autre façon de démontrer la puissance de votre API. Par exemple, écrivez une telle application et partagez sa source, mais n'exécutez pas réellement ce code. Assurez-vous cependant qu'il est bien documenté, afin que tout le monde puisse le déployer et le voir fonctionner (sous réserve de la limitation).

L'application que vous exécutez devra être refactorisée pour éviter les demandes d'API côté client et être plus rendue par le serveur. Vous pouvez toujours dogfood votre API, mais pas de manière évidente - faire des demandes sécurisées à l'API sans étranglement du côté serveur.

Ajustez la limitation de débit pour permettre à votre application de fonctionner et d'investir dans l'optimisation des performances pour gérer la charge.

Et oui, ayez d'abord l'API de base sans étranglement et conservez-la dans un réseau privé. Limiter dans une couche distincte accessible au public.

8
Anton Strogonoff

Vous pouvez essayer de générer un ID de session unique, lié à une certaine adresse IP/utilisateur et un temps limité pour vivre. Lorsqu'un utilisateur télécharge le code JavaScript de votre application, injectez l'ID de session généré dans le code source JavaScript. L'ID de session sera attaché à chaque demande de votre API et la limite de taux est levée.

L'ID ne peut pas être simplement copié pour usurpation d'identité, car il n'est valable que pour une seule adresse IP, un seul utilisateur et une durée limitée. Ainsi, un adversaire devrait appeler votre page et filtrer la clé de votre source JavaScript ou d'intercepter la demande Ajax chaque fois qu'un nouvel utilisateur souhaite l'utiliser.

ne autre option:

Configurez un proxy pour votre propre application et utilisez l'obscurcissement. Les demandes Ajax au proxy utilisent des noms différents des vrais appels API et le proxy les traduit à nouveau. Ainsi, votre application n'appellera pas getDocument sur votre véritable API, mais elle appellera getFELSUFDSKJE sur votre proxy. Le proxy convertira cet appel en getDocument et le transmettra à l'API à débit limité réel.

Votre API réelle ne limitera pas les demandes de taux par le proxy.

Et pour que d'autres personnes n'utilisent pas votre proxy pour leur propre application, vous modifiez quotidiennement le schéma d'obscurcissement. Les noms d'appels obscurcis peuvent être générés automatiquement dans votre code source JavaScript et configurés dans le proxy.

Un client souhaitant utiliser cela devrait également suivre l'évolution de votre obfuscation pour utiliser votre proxy. Et vous pouvez toujours utiliser des en-têtes de référent et similaires pour la journalisation, afin que vous puissiez trouver des personnes utilisant votre proxy. Ou attrapez-les lorsque vous modifiez le schéma d'obscurcissement.

4
Falco

Pouvez-vous mettre en place une instance distincte de l'interface utilisateur et de l'API sans étranglement, puis restreindre l'accès aux adresses IP provenant de votre organisation?

Par exemple, déployez le tout derrière votre pare-feu d'entreprise et attachez l'application à la même base de données que l'instance publique si vous avez besoin de partager des données entre des instances.

4
Willie Wheeler
  • Adresses IP source sur liste blanche
  • Utilisez un VPN , liste blanche des membres VPN
  • Une solution de proxy ou un module complémentaire de navigateur qui ajoute des en-têtes HTTP devrait être correct si vous pouvez sécuriser le proxy et ne vous inquiétez pas MITM attaques reniflant le trafic
  • Toute solution impliquant des secrets peut atténuer l'impact des fuites en faisant tourner quotidiennement les secrets
3
the8472

Configurez plusieurs comptes et choisissez-en un au hasard à chaque demande, ou changez celui que vous utilisez toutes les heures environ. De cette façon, vous pouvez répartir la charge sur les comptes n, vous donnant jusqu'à n fois des limites plus élevées.

Faites attention à ne pas vous arrêter accidentellement si vous essayez de trouver d'autres utilisateurs faisant cela, si ce n'est pas autorisé pour les clients.

2
Filip Haglund