Nehmen wir an, Sie codieren eine Funktion, die Eingaben von einer externen API entgegennimmt MyAPI
.
Diese externe API MyAPI
hat einen Vertrag, der besagt, dass sie a string
oder a zurückgibt number
.
Ist es gegen Dinge zu schützen empfohlen wie null
, undefined
, boolean
usw. , auch wenn sie nicht Teil der API von ist MyAPI
? Insbesondere, da Sie keine Kontrolle über diese API haben, können Sie die Garantie nicht durch eine statische Typprüfung abgeben, sodass es besser ist, auf Nummer sicher zu gehen, als sich zu entschuldigen.
Ich denke in Bezug auf das Robustheitsprinzip .
design
api
api-design
web-services
functions
Adam Thompson
quelle
quelle
<!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>
Antworten:
Sie sollten den Eingaben in Ihre Software niemals vertrauen, unabhängig von der Quelle. Es ist nicht nur wichtig, die Typen zu validieren, sondern auch die Eingabebereiche und die Geschäftslogik. Per Kommentar wird dies von OWASP gut beschrieben
Wenn Sie dies nicht tun, bleiben Ihnen bestenfalls Mülldaten, die Sie später bereinigen müssen. Im schlimmsten Fall können Sie jedoch böswillige Angriffe ausführen, wenn dieser Upstream-Dienst in irgendeiner Weise kompromittiert wird (siehe Target-Hack). Der Bereich der dazwischen liegenden Probleme umfasst das Versetzen Ihrer Anwendung in einen nicht behebbaren Zustand.
Aus den Kommentaren kann ich ersehen, dass meine Antwort vielleicht etwas erweitert werden könnte.
Mit "Niemals den Eingaben vertrauen" meine ich einfach, dass Sie nicht davon ausgehen können, dass Sie immer gültige und vertrauenswürdige Informationen von vor- oder nachgelagerten Systemen erhalten. Daher sollten Sie diese Eingaben immer nach besten Kräften bereinigen oder ablehnen es.
Ein Argument tauchte in den Kommentaren auf, auf die ich als Beispiel eingehen werde. Ja, Sie müssen Ihrem Betriebssystem bis zu einem gewissen Grad vertrauen. Es ist jedoch nicht unangemessen, die Ergebnisse eines Zufallsgenerators beispielsweise abzulehnen, wenn Sie eine Zahl zwischen 1 und 10 anfordern und dieser mit "bob" antwortet.
In ähnlicher Weise sollten Sie im Falle des OP auf jeden Fall sicherstellen, dass Ihre Anwendung nur gültige Eingaben vom Upstream-Service akzeptiert. Was Sie tun, wenn es nicht in Ordnung ist, liegt bei Ihnen und hängt in hohem Maße von der tatsächlichen Geschäftsfunktion ab, die Sie ausführen möchten. Sie würden es jedoch minimal für das spätere Debuggen protokollieren und ansonsten sicherstellen, dass Ihre Anwendung nicht funktioniert in einen nicht wiederherstellbaren oder unsicheren Zustand.
Sie können zwar nie alle möglichen Eingaben kennen, die Ihnen jemand / etwas geben könnte, aber Sie können auf jeden Fall die zulässigen Eingaben basierend auf den Geschäftsanforderungen einschränken und auf dieser Grundlage eine Art von Eingabe-Whitelist durchführen.
quelle
Ja natürlich Aber warum denkst du, dass die Antwort anders sein könnte?
Sie möchten sicher nicht, dass sich Ihr Programm auf unvorhersehbare Weise verhält, falls die API nicht das zurückgibt, was der Vertrag sagt, oder? Zumindest muss man sich also irgendwie mit einem solchen Verhalten auseinandersetzen . Eine minimale Form der Fehlerbehandlung ist immer die (sehr minimale!) Anstrengung wert, und es gibt absolut keine Entschuldigung, so etwas nicht zu implementieren.
Wie viel Aufwand Sie investieren sollten, um einen solchen Fall zu bewältigen, hängt jedoch stark vom Einzelfall ab und kann nur im Kontext Ihres Systems beantwortet werden. Oft kann ein kurzer Protokolleintrag und das ordnungsgemäße Beenden der Anwendung ausreichen. Manchmal ist es besser, eine detaillierte Ausnahmebehandlung zu implementieren, mit verschiedenen Formen von "falschen" Rückgabewerten umzugehen und möglicherweise eine Ausweichstrategie zu implementieren.
Es macht jedoch einen großen Unterschied, wenn Sie nur eine interne Tabellenkalkulationsanwendung schreiben, die von weniger als 10 Personen verwendet werden soll und deren finanzielle Auswirkungen durch einen Anwendungsabsturz recht gering sind, oder wenn Sie ein neues autonomes Autofahren erstellen System, bei dem ein Anwendungsabsturz Leben kosten kann.
Es gibt also keine Abkürzung , um darüber nachzudenken , was Sie tun . Die Verwendung Ihres gesunden Menschenverstands ist immer obligatorisch.
quelle
Das Robustness-Prinzip - insbesondere die Hälfte davon - ist eine sehr schlechte Idee in der Software. Es wurde ursprünglich im Zusammenhang mit Hardware entwickelt, bei der aufgrund physikalischer Einschränkungen technische Toleranzen sehr wichtig sind. Bei Software haben Sie jedoch zwei Möglichkeiten, wenn Sie von jemandem eine fehlerhafte oder anderweitig falsche Eingabe erhalten. Sie können es entweder ablehnen (vorzugsweise mit einer Erklärung, was schief gelaufen ist) oder versuchen, herauszufinden, was es bedeuten sollte.
Wählen Sie niemals, niemals, niemals diese zweite Option, es sei denn, Sie verfügen über Ressourcen, die dem Google Search-Team entsprechen, um Ihr Projekt zu bearbeiten. (Und selbst dann haben die Vorschläge von Google das Gefühl, dass sie ungefähr die Hälfte der Zeit direkt aus dem linken Feld herauskommen.) Wenn Sie dies versuchen, werden Sie am Ende massive Kopfschmerzen haben, bei denen Ihr Programm häufig versucht, sie zu interpretieren schlechte Eingabe als X, wenn der Absender wirklich Y meinte.
Das ist aus zwei Gründen schlecht. Die offensichtliche ist, dass Sie dann schlechte Daten in Ihrem System haben. Das weniger offensichtliche ist, dass in vielen Fällen weder Sie noch der Absender bemerken, dass etwas schief gelaufen ist, bis viel später etwas in die Luft sprengt und Sie plötzlich eine große, teure Verwirrung haben, die behoben werden muss und keine Ahnung haben was schief gelaufen ist, weil der wahrnehmbare Effekt so weit von der eigentlichen Ursache entfernt ist.
Aus diesem Grund gibt es das Fail-Fast-Prinzip. Sparen Sie allen Beteiligten die Kopfschmerzen, indem Sie sie auf Ihre APIs anwenden.
quelle
Im Allgemeinen sollte Code so erstellt werden, dass nach Möglichkeit mindestens die folgenden Einschränkungen eingehalten werden:
Bei richtiger Eingabe korrekte Ausgabe erzeugen.
Wenn Sie eine gültige Eingabe erhalten (die möglicherweise korrekt ist oder nicht), erzeugen Sie (ebenfalls) eine gültige Ausgabe.
Verarbeiten Sie ungültige Eingaben ohne Nebenwirkungen, die über die normalen Eingaben oder die als Signal für einen Fehler definierten hinausgehen.
In vielen Situationen durchlaufen Programme im Wesentlichen verschiedene Datenblöcke, ohne besonders darauf zu achten, ob sie gültig sind. Wenn solche Chunks ungültige Daten enthalten, enthält die Programmausgabe wahrscheinlich ungültige Daten. Programme, die ihre Ausgabe verarbeiten, sollten die Möglichkeit ungültiger Daten in sich berücksichtigen , es sei denn, ein Programm wurde speziell für die Validierung aller Daten entwickelt und gewährleistet, dass auch bei ungültiger Eingabe keine ungültige Ausgabe erfolgt .
Die frühzeitige Validierung von Daten ist häufig wünschenswert, jedoch nicht immer besonders praktisch. Unter anderem, wenn die Gültigkeit eines Datenblocks vom Inhalt anderer Datenblöcke abhängt und der Großteil der Daten, die in eine bestimmte Abfolge von Schritten eingegeben werden, auf dem Weg herausgefiltert wird, wodurch die Gültigkeit auf die Daten beschränkt wird, die den Datenblock durchlaufen Alle Stufen können eine viel bessere Leistung erbringen als der Versuch, alles zu validieren.
Selbst wenn erwartet wird, dass ein Programm nur vorab validierte Daten erhält, ist es oft gut, wenn es die oben genannten Einschränkungen trotzdem einhält, wann immer dies praktikabel ist. Das Wiederholen der vollständigen Validierung bei jedem Verarbeitungsschritt ist oftmals ein erheblicher Leistungsverlust, aber der begrenzte Umfang der Validierung, der zur Einhaltung der oben genannten Einschränkungen erforderlich ist, ist möglicherweise viel billiger.
quelle
Lassen Sie uns die beiden Szenarien vergleichen und versuchen, zu einer Schlussfolgerung zu gelangen.
Szenario 1 Unsere Anwendung geht davon aus, dass sich die externe API gemäß der Vereinbarung verhält.
Szenario 2 Unsere Anwendung geht davon aus, dass sich die externe API fehlerhaft verhalten kann. Fügen Sie daher Vorsichtsmaßnahmen hinzu.
Im Allgemeinen besteht die Möglichkeit, dass eine API oder Software gegen die Vereinbarungen verstößt. Möglicherweise liegt ein Fehler oder ein unerwarteter Zustand vor. Sogar eine API weist möglicherweise Probleme in den internen Systemen auf, die zu unerwarteten Ergebnissen führen.
Wenn unser Programm unter der Annahme geschrieben wurde, dass die externe API die Vereinbarungen einhält und keine Vorsichtsmaßnahmen trifft; Wer wird die Partei sein, die sich mit den Problemen befasst? Wir werden es sein, die Integrationscode geschrieben haben.
Zum Beispiel die Nullwerte, die Sie ausgewählt haben. Angenommen, gemäß der API-Vereinbarung sollte die Antwort Werte ungleich Null haben. Wenn es jedoch plötzlich verletzt wird, führt unser Programm zu NPEs.
Ich bin daher der Meinung, dass es besser ist, sicherzustellen, dass Ihre Anwendung über zusätzlichen Code für unerwartete Szenarien verfügt.
quelle
Sie sollten eingehende Daten - vom Benutzer eingegeben oder auf andere Weise - immer validieren, damit Sie über einen Prozess verfügen, mit dem Sie umgehen können, wenn die von dieser externen API abgerufenen Daten ungültig sind.
Im Allgemeinen sollte jede Naht, in der sich organisationsfremde Systeme treffen, Authentifizierung, Autorisierung (falls nicht einfach durch Authentifizierung definiert) und Validierung erfordern.
quelle
Im Allgemeinen müssen Sie sich immer vor fehlerhaften Eingaben schützen, aber je nach Art der API bedeutet "Schutz" unterschiedliche Dinge.
Für eine externe API zu einem Server möchten Sie nicht versehentlich einen Befehl erstellen, der den Status des Servers zum Absturz bringt oder gefährdet. Sie müssen sich also davor schützen.
Für eine API wie z. B. eine Containerklasse (Liste, Vektor usw.) ist das Auslösen von Ausnahmen ein perfektes Ergebnis. Eine Beeinträchtigung des Status der Klasseninstanz kann in gewissem Maße akzeptabel sein (z. B. ein sortierter Container, der mit einem fehlerhaften Vergleichsoperator versehen ist, wird dies nicht tun) sortiert werden), kann sogar ein Absturz der Anwendung akzeptabel sein, aber eine Beeinträchtigung des Anwendungszustands - z. B. das Schreiben in zufällige Speicherorte, die nicht mit der Klasseninstanz zusammenhängen - ist höchstwahrscheinlich nicht möglich.
quelle
Um eine etwas abweichende Meinung zu vertreten: Ich denke, es kann akzeptabel sein, nur mit den Daten zu arbeiten, die Sie erhalten, auch wenn sie gegen den Vertrag verstoßen. Dies hängt von der Verwendung ab: Es muss ein String für Sie sein, oder es ist etwas, das Sie nur anzeigen / nicht verwenden usw. In letzterem Fall akzeptieren Sie es einfach. Ich habe eine API, die nur 1% der von einer anderen API gelieferten Daten benötigt. Es ist mir egal, welche Art von Daten in den 99% sind, also werde ich sie nie überprüfen.
Es muss ein Gleichgewicht bestehen zwischen "Fehler haben, weil ich meine Eingaben nicht genug überprüfe" und "Ich lehne gültige Daten ab, weil ich zu streng bin".
quelle
Ich gehe davon aus, dass ich immer jeden einzelnen Eingang meines Systems überprüfe. Das bedeutet, dass jeder von einer API zurückgegebene Parameter überprüft werden sollte, auch wenn mein Programm ihn nicht verwendet. Ich neige auch dazu, jeden Parameter, den ich an eine API sende, auf Richtigkeit zu überprüfen. Es gibt nur zwei Ausnahmen von dieser Regel, siehe unten.
Der Grund für das Testen ist, dass mein Programm sich aus irgendeinem Grund nicht auf irgendetwas verlassen kann, wenn die API / Eingabe falsch ist. Vielleicht war mein Programm mit einer alten Version der API verknüpft, die etwas anderes macht, als ich glaube? Vielleicht ist mein Programm über einen Fehler im externen Programm gestolpert, der noch nie passiert ist. Oder noch schlimmer, passiert die ganze Zeit, aber niemand kümmert sich darum! Vielleicht wird das externe Programm von einem Hacker getäuscht, um Dinge zurückzugeben, die mein Programm oder das System beschädigen können?
Die zwei Ausnahmen, um alles in meiner Welt zu testen, sind:
Leistung nach sorgfältiger Messung der Leistung:
Wenn Sie keine Ahnung haben, was Sie mit einem Fehler tun sollen
Wie genau Eingaben / Rückgabewerte überprüft werden müssen, ist eine wichtige Frage. Wenn die API beispielsweise eine Zeichenfolge zurückgeben soll, würde ich Folgendes überprüfen:
Der Datentyp Actully ist ein String
und diese Länge liegt zwischen min und max Werten. Überprüfen Sie die Zeichenfolgen immer auf die maximale Größe, die von meinem Programm erwartet wird (die Rückgabe zu großer Zeichenfolgen ist ein klassisches Sicherheitsproblem in vernetzten Systemen).
Einige Zeichenfolgen sollten auf "unzulässige" Zeichen oder Inhalte überprüft werden, wenn dies relevant ist. Wenn Ihr Programm die Zeichenfolge möglicherweise sendet, um später eine Datenbank zu melden, sollten Sie nach Datenbankangriffen suchen (nach SQL-Injection suchen). Diese Tests werden am besten an den Grenzen meines Systems durchgeführt, wo ich feststellen kann, woher der Angriff stammt, und ich kann frühzeitig scheitern. Es kann schwierig sein, einen vollständigen SQL-Injection-Test durchzuführen, wenn Zeichenfolgen später kombiniert werden. Daher sollte dieser Test vor dem Aufrufen der Datenbank durchgeführt werden. Wenn Sie jedoch frühzeitig Probleme feststellen, kann dies hilfreich sein.
Der Grund für das Testen von Parametern, die ich an die API sende, besteht darin, sicherzustellen, dass ich ein korrektes Ergebnis zurückerhalte. Auch diese Tests vor dem Aufrufen einer API sind möglicherweise unnötig, erfordern jedoch sehr wenig Leistung und können Fehler in meinem Programm erkennen. Daher sind die Tests bei der Entwicklung eines Systems am wertvollsten (aber heutzutage scheint sich jedes System in ständiger Entwicklung zu befinden). Abhängig von den Parametern können die Tests mehr oder weniger gründlich sein, aber ich finde, dass Sie häufig zulässige Min- und Max-Werte für die meisten Parameter festlegen können, die mein Programm erstellen könnte. Vielleicht sollte ein String immer mindestens 2 Zeichen haben und maximal 2000 Zeichen lang sein? Das Minimum und Maximum sollte innerhalb der API liegen, da ich weiß, dass mein Programm niemals den vollen Bereich einiger Parameter verwenden wird.
quelle