Disons que vous codez une fonction qui prend l’entrée d’une API externe MyAPI
.
Cette API externe MyAPI
a un contrat qui stipule qu’elle renverra un string
ou number
.
Est - il recommandé de se prémunir contre des choses comme null
, undefined
, boolean
, etc. , même si elle ne fait pas partie de l'API MyAPI
? En particulier, étant donné que vous n'avez aucun contrôle sur cette API, vous ne pouvez pas donner la garantie par une analyse du type statique, il est donc préférable d'être prudent que de s'excuser.
Je pense au principe de robustesse .
design
api
api-design
web-services
functions
Adam Thompson
la source
la source
<!doctype html><html><head><title>504 Gateway Timeout</title></head><body>The server was unable to process your request. Make sure you have typed the address correctly. If the problem persists, please try again later.</body></html>
Réponses:
Vous ne devriez jamais faire confiance aux entrées de votre logiciel, quelle que soit sa source. Non seulement la validation des types est importante, mais également la plage des entrées et la logique métier. Par un commentaire, ceci est bien décrit par OWASP
Dans le meilleur des cas, vous conserverez au mieux des données erronées que vous devrez nettoyer plus tard, mais au pire, vous laisserez une possibilité d'exploits malveillants si ce service en amont est compromis d'une manière ou d'une autre (qv the hack Target). L’éventail des problèmes entre ces opérations inclut l’obtention de votre application dans un état irrécupérable.
D'après les commentaires, je peux voir que ma réponse pourrait peut-être utiliser un peu de développement.
Par "ne faites jamais confiance aux entrées", je veux simplement dire que vous ne pouvez pas présumer que vous recevrez toujours des informations valides et dignes de confiance des systèmes en amont ou en aval. Par conséquent, vous devez toujours optimiser ces entrées, ou les rejeter. il.
Un argument est apparu dans les commentaires que je vais aborder à titre d'exemple. Bien que oui, vous devez faire confiance à votre système d'exploitation dans une certaine mesure, il n'est pas déraisonnable, par exemple, de rejeter les résultats d'un générateur de nombres aléatoires si vous lui demandez un nombre compris entre 1 et 10 et qu'il répond par "bob".
De même, dans le cas de l'OP, vous devez absolument vous assurer que votre application n'accepte que des entrées valides du service en amont. Ce que vous faites quand ce n'est pas OK est à vous de décider et dépend en grande partie de la fonction commerciale que vous essayez d'accomplir, mais vous devez le consigner au minimum pour un débogage ultérieur et vous assurer que votre application ne fonctionne pas. dans un état irrécupérable ou précaire.
Bien que vous ne puissiez jamais connaître toutes les entrées possibles que quelqu'un ou quelque chose puisse vous donner, vous pouvez certainement limiter ce qui est permis en fonction des besoins de l'entreprise et créer une sorte de liste blanche d'entrée basée sur ces informations.
la source
Oui bien sur. Mais qu'est-ce qui vous fait penser que la réponse pourrait être différente?
Vous ne voulez sûrement pas laisser votre programme se comporter de manière imprévisible au cas où l'API ne renverrait pas ce que le contrat dit, n'est-ce pas? Donc , au moins vous avez à traiter avec le comportement d'une telle façon ou d' autre . Une forme minimale de traitement des erreurs vaut toujours l'effort (très minime!), Et il n'y a absolument aucune excuse pour ne pas mettre en œuvre quelque chose comme ça.
Toutefois, les efforts que vous devez déployer pour traiter un tel cas dépendent fortement de votre cas et ne peuvent être résolus que dans le contexte de votre système. Souvent, une courte entrée dans le journal et laisser l’application se terminer normalement peuvent suffire. Il est parfois préférable de mettre en œuvre une gestion détaillée des exceptions, de traiter différentes formes de valeurs de retour «erronées» et éventuellement de mettre en œuvre une stratégie de secours.
Mais cela fait une énorme différence si vous écrivez juste une application de formatage de feuille de calcul interne, devant être utilisée par moins de 10 personnes et où l'impact financier d'un blocage d'application est assez faible, ou si vous créez une nouvelle conduite autonome. système, où une panne d'application peut coûter des vies.
Il n’ya donc pas de raccourci pour réfléchir à ce que vous faites , il est toujours impératif d’utiliser votre bon sens.
la source
Le principe de robustesse - en particulier la moitié "soyez libéraux dans ce que vous acceptez" - est une très mauvaise idée dans le logiciel. Il a été développé à l'origine dans le contexte du matériel, où les contraintes physiques rendent les tolérances d'ingénierie très importantes, mais dans le logiciel, lorsque quelqu'un vous envoie une entrée mal formée ou incorrecte, vous avez le choix. Vous pouvez soit le rejeter (de préférence avec une explication de ce qui ne va pas), soit vous pouvez essayer de comprendre ce que cela était supposé vouloir dire.
Jamais, jamais, jamais, ne choisissez cette deuxième option que si vous disposez de ressources équivalentes à celles de l'équipe de recherche de Google, car c'est ce qu'il faut pour créer un programme informatique qui fasse tout ce qui est proche d'un travail décent dans ce domaine problématique. (Et même dans ce cas, les suggestions de Google donnent l'impression qu'elles sortent du champ gauche environ la moitié du temps.) Si vous essayez de le faire, vous allez vous retrouver avec un mal de tête énorme où votre programme tentera souvent d'interpréter mauvaise entrée en tant que X, alors que l'expéditeur voulait vraiment dire Y.
C'est mauvais pour deux raisons. La plus évidente est parce que vous avez alors de mauvaises données dans votre système. La moins évidente est que dans de nombreux cas, ni vous ni l'expéditeur ne réaliserez que quelque chose ne va pas bien avant que beaucoup plus tard ne se produise, quand quelque chose explose à la figure, puis vous avez soudainement un gros désordre coûteux à réparer et aucune idée ce qui a mal tourné parce que l’effet notable est si loin de la cause première.
C'est pourquoi le principe Fail Fast existe; épargnez le mal de tête à toutes les personnes concernées en l'appliquant à vos API.
la source
En général, un code devrait être construit pour respecter au moins les contraintes suivantes chaque fois que cela est possible:
Lorsque l'entrée est correcte, produisez une sortie correcte.
Lorsqu'une entrée valide est donnée (que cela soit correct ou non), produit une sortie valide (de même).
Lorsque l'entrée est invalide, traitez-la sans aucun effet secondaire autre que ceux causés par une entrée normale ou ceux définis comme signalant une erreur.
Dans de nombreuses situations, les programmes vont essentiellement passer par différents morceaux de données sans se soucier de leur validité. Si de tels morceaux contiennent des données non valides, la sortie du programme contiendra probablement des données non valides. À moins qu'un programme ne soit spécifiquement conçu pour valider toutes les données et garantir qu'il ne produira pas de sortie invalide même si une entrée invalide lui est fournie , les programmes qui traitent sa sortie devraient permettre la possibilité que des données invalides s'y trouvent.
Bien que la validation précoce des données soit souvent souhaitable, elle n’est pas toujours particulièrement pratique. Entre autres choses, si la validité d’un bloc de données dépend du contenu d’autres morceaux et si la majorité des données introduites dans une séquence d’étapes seront filtrées en cours de route, limitant ainsi la validation aux données qui le permettent. toutes les étapes peuvent donner de bien meilleures performances que d'essayer de tout valider.
De plus, même si un programme ne doit recevoir que des données pré-validées, il est souvent bon de le laisser respecter les contraintes ci-dessus de toute façon, chaque fois que possible. Répéter la validation complète à chaque étape du traitement serait souvent une perte de performances majeure, mais la quantité limitée de validation nécessaire pour respecter les contraintes ci-dessus peut coûter beaucoup moins cher.
la source
Comparons les deux scénarios et essayons de tirer une conclusion.
Scénario 1 Notre application suppose que l'API externe se comportera conformément à l'accord.
Scénario 2 Notre application suppose que l'API externe peut mal se comporter, d'où l'ajout de précautions.
En général, une API ou un logiciel peut enfreindre les contrats. peut être dû à un bogue ou à des conditions inattendues. Même une API peut avoir des problèmes dans les systèmes internes, ce qui entraîne des résultats inattendus.
Si notre programme est écrit en supposant que l’API externe respecte les accords et évite d’ajouter des précautions; qui sera la partie face aux problèmes? Ce sera nous, ceux qui ont écrit le code d'intégration.
Par exemple, les valeurs NULL que vous avez choisies. Selon l'accord de l'API, la réponse doit avoir des valeurs non nulles. mais s'il est violé soudainement, notre programme produira des NPE.
Je pense donc qu'il sera préférable de s'assurer que votre application dispose d'un code supplémentaire pour traiter les scénarios inattendus.
la source
Vous devez toujours valider les données entrantes - saisies par l'utilisateur ou autre - afin de disposer d'un processus permettant de gérer le moment où les données extraites de cette API externe sont invalides.
De manière générale, toute couture où se rencontrent des systèmes extra-organisationnels doit nécessiter une authentification, une autorisation (si elle n'est pas simplement définie par authentification) et une validation.
la source
En général, oui, vous devez toujours vous prémunir contre des entrées erronées, mais selon le type d'API, «garde» signifie différentes choses.
Pour une API externe à un serveur, vous ne souhaitez pas créer accidentellement une commande qui bloque ou compromet l'état du serveur. Vous devez donc vous protéger contre cela.
Pour une API telle que, par exemple, une classe de conteneur (liste, vecteur, etc.), le lancement d’exceptions est un résultat parfait, compromettre l’état de l’instance de la classe peut être acceptable dans une certaine mesure (par exemple, un conteneur trié doté d’un opérateur de comparaison défaillant ne trié), même planter l’application peut être acceptable, mais compromettre l’état de l’application - par exemple écrire dans des emplacements de mémoire aléatoires non liés à l’instance de la classe - n’est fort probablement pas.
la source
Pour donner un avis légèrement différent: je pense qu’il est acceptable de travailler uniquement avec les données qui vous sont fournies, même si cela ne respecte pas son contrat. Cela dépend de l'utilisation: c'est quelque chose qui DOIT être une chaîne pour vous, ou est-ce quelque chose que vous êtes en train d'afficher / que vous n'utilisez pas, etc. Dans ce dernier cas, acceptez-le simplement. J'ai une API qui nécessite seulement 1% des données fournies par une autre API. Je me fiche de savoir quel genre de données sont dans les 99%, donc je ne les vérifierai jamais.
Il doit y avoir un équilibre entre "avoir des erreurs parce que je ne vérifie pas assez mes entrées" et "je refuse les données valides parce que je suis trop strict".
la source
Mon point de vue est de toujours, toujours vérifier chaque entrée de mon système. Cela signifie que chaque paramètre renvoyé par une API doit être vérifié, même si mon programme ne l'utilise pas. J'ai aussi tendance à vérifier l'exactitude de chaque paramètre que j'envoie à une API. Il n'y a que deux exceptions à cette règle, voir ci-dessous.
La raison du test est que si, pour une raison quelconque, l’API / entrée est incorrecte, mon programme ne peut s’appuyer sur rien. Peut-être que mon programme était lié à une ancienne version de l'API qui fait quelque chose de différent de ce que je crois? Peut-être que mon programme est tombé sur un bogue dans le programme externe qui n’était jamais arrivé auparavant. Ou pire encore, cela arrive tout le temps mais personne ne s'en soucie! Peut-être que le programme externe se laisse berner par un pirate informatique pour renvoyer des éléments susceptibles de nuire à mon programme ou au système?
Les deux exceptions à tout tester dans mon monde sont:
Performance après mesure minutieuse de la performance:
Quand vous ne savez pas quoi faire avec une erreur
Exactement, comment vérifier avec soin les entrées / valeurs renvoyées est une question importante. Par exemple, si l’API est supposée renvoyer une chaîne, je vérifierais que:
le type de données actully est une chaîne
et cette longueur est comprise entre les valeurs min et max. Vérifiez toujours la taille maximale des chaînes que mon programme peut s'attendre à gérer (le renvoi de chaînes trop grandes est un problème de sécurité classique dans les systèmes en réseau).
Certaines chaînes doivent être vérifiées pour les caractères "illégaux" ou le contenu lorsque cela est pertinent. Si votre programme peut envoyer la chaîne pour dire une base de données ultérieurement, il est judicieux de vérifier les attaques de base de données (recherche d'injection SQL). Ces tests s’effectuent mieux aux frontières de mon système, où je peux localiser l’attaque et où je peux échouer plus tôt. Effectuer un test d'injection SQL complet peut s'avérer difficile lorsque les chaînes sont combinées ultérieurement. Ce test doit donc être effectué avant d'appeler la base de données. Toutefois, si vous pouvez détecter certains problèmes plus tôt, cela peut s'avérer utile.
Les paramètres de test que j’envoie à l’API sont testés pour que le résultat obtenu soit correct. Encore une fois, effectuer ces tests avant d'appeler une API peut sembler inutile, mais cela prend très peu de performances et risque de contenir des erreurs dans mon programme. Par conséquent, les tests sont plus précieux lors du développement d’un système (mais à présent, tous les systèmes semblent être en développement continu). Selon les paramètres, les tests peuvent être plus ou moins approfondis, mais j’ai tendance à penser que vous pouvez souvent définir des valeurs minimales et maximales autorisées pour la plupart des paramètres que mon programme pourrait créer. Peut-être qu'une chaîne devrait toujours avoir au moins 2 caractères et une longueur maximale de 2000 caractères? Les valeurs min et maximum doivent correspondre à celles permises par l'API, car je sais que mon programme n'utilisera jamais toute la gamme de certains paramètres.
la source