Dies ist der zweite Blogbeitrag in unserer Reihe über den Einsatz von NGINX Open Source und NGINX Plus als API-Gateway.
- Teil 1 enthält detaillierte Konfigurationsanweisungen für verschiedene Anwendungsfälle.
- Dieser ursprünglich 2018 veröffentlichte Beitrag wurde aktualisiert, um die aktuellen Best Practices für die API-Konfiguration widerzuspiegeln. Dabei werden verschachtelte
location-Blöcke anstelle von Rewrite-Regeln zum Weiterleiten von Anfragen verwendet.
Dieser Beitrag erweitert diese Anwendungsfälle und befasst sich mit verschiedenen Schutzmaßnahmen, die zum Schutz und zur Absicherung von Backend-API-Diensten in Produktionsumgebungen eingesetzt werden können:
- Ratenbegrenzung
- Durchsetzung bestimmter Anfragemethoden
- Anwenden einer granularen Zugriffskontrolle
- Anfragegrößen steuern
- Validieren von Anfragetexten
- Teil 3 erläutert, wie NGINX Open Source und NGINX Plus als API-Gateway für gRPC-Dienste bereitgestellt werden.
Hinweis: Sofern nicht anders angegeben, gelten alle Informationen in diesem Beitrag sowohl für NGINX Open Source als auch für NGINX Plus. Zur besseren Lesbarkeit wird im weiteren Verlauf des Blogbeitrags einfach von „NGINX“ gesprochen.
Ratenbegrenzung
Im Gegensatz zu browserbasierten Clients können einzelne API-Clients Ihre APIs enorm belasten und dabei sogar einen so großen Anteil der Systemressourcen beanspruchen, dass andere API-Clients praktisch keinen Zugriff mehr haben. Diese Gefahr geht nicht nur von böswilligen Clients aus: Auch ein fehlerhaft arbeitender oder programmierter API-Client kann in eine Schleife geraten, die das Backend überlastet. Um dies zu verhindern, begrenzen wir die Anfragerate. So gewährleisten wir eine faire Nutzung durch jeden Client und schützen die Ressourcen der Backend-Dienste.
NGINX kann Ratenbegrenzungen anhand eines beliebigen Attributs der Anfrage anwenden. In der Regel wird die IP-Adresse des Clients verwendet. Wenn jedoch die Authentifizierung für die API aktiviert ist, stellt die authentifizierte Client-ID ein zuverlässigeres und genaueres Attribut dar.
Die Ratenbegrenzungen selbst werden in der übergeordneten Konfigurationsdatei des API-Gateways definiert und können anschließend global, für einzelne APIs oder sogar für einzelne URIs angewendet werden.
Codebeispiel wird geladen…
In diesem Beispiel definiert die Direktive limit_req_zone in Zeile 4 eine Ratenbegrenzung von 10 Anfragen pro Sekunde für jede Client-IP-Adresse ($binary_remote_addr). Die Direktive in Zeile 5 definiert eine Begrenzung von 200 Anfragen pro Sekunde für jede authentifizierte Client-ID ($http_apikey). Dies veranschaulicht, wie sich mehrere Ratenbegrenzungen unabhängig von ihrem Anwendungsort definieren lassen. Eine API kann mehrere Ratenbegrenzungen gleichzeitig oder unterschiedliche Ratenbegrenzungen für verschiedene Ressourcen anwenden.
Im folgenden Konfigurationsausschnitt verwenden wir dann die limit_req-Direktive, um die erste Ratenbegrenzung im Richtlinienabschnitt der in Teil 1<.htmla> beschriebenen „Warehouse API“ anzuwenden. Standardmäßig sendet NGINX die Antwort 503 (Service Unavailable), wenn die Ratenbegrenzung überschritten wurde. Für API-Clients ist es jedoch hilfreich, ausdrücklich zu erfahren, dass sie ihre Ratenbegrenzung überschritten haben, damit sie ihr Verhalten entsprechend anpassen können. Zu diesem Zweck verwenden wir die limit_req_status-Direktive, um stattdessen die Antwort 429 (Too Many Requests) zu senden.
Codebeispiel wird geladen…
Sie können zusätzliche Parameter für die Direktive „limit_req“ verwenden, um die Durchsetzung von Anfrageratenbegrenzungen durch NGINX präzise anzupassen. Beispielsweise können Anfragen bei Überschreitung des Grenzwerts in eine Warteschlange gestellt werden, anstatt sie sofort abzulehnen. So bleibt Zeit, bis die Anfragerate wieder unter den festgelegten Grenzwert fällt. Weitere Informationen zur Feinabstimmung von Anfrageratenbegrenzungen finden Sie im Beitrag „Anfrageratenbegrenzung mit NGINX und NGINX Plus“ in unserem Blog.
Bestimmte Anfragemethoden erzwingen
Bei RESTful APIs ist die HTTP-Methode (auch Verb genannt) ein wichtiger Bestandteil jedes API-Aufrufs und von großer Bedeutung für die API-Definition. Nehmen wir als Beispiel den Preisberechnungsdienst unserer Warehouse API:
GET/api/warehouse/pricing/item001gibt den Preis von item001 zurückPATCH/api/warehouse/pricing/item001ändert den Preis von item001
Wir können die URI-Routing-Definitionen in der Warehouse API so aktualisieren, dass bei Anfragen an den Preisgestaltungsdienst nur diese beiden HTTP-Methoden akzeptiert werden (und bei Anfragen an den Bestandsdienst nur die Methode GET).
Codebeispiel wird geladen…
Mit dieser Konfiguration werden Anfragen an den Preisgestaltungsdienst, die andere als die in Zeile 22 aufgeführten Methoden verwenden (sowie Anfragen an den Bestandsdienst, die eine andere als die in Zeile 13 aufgeführte Methode verwenden), abgelehnt und nicht an die Backend-Dienste weitergeleitet. NGINX sendet die Antwort 405 (Methode nicht zulässig), um den API-Client über die genaue Art des Fehlers zu informieren, wie die folgende Konsolenausgabe zeigt. Wenn eine Sicherheitsrichtlinie mit minimaler Offenlegung erforderlich ist, kann die Direktive error_page verwendet werden, um diese Antwort stattdessen in eine weniger aussagekräftige Fehlermeldung umzuwandeln, zum Beispiel 400 (Ungültige Anfrage).
$ curl https://api.example.com/api/warehouse/pricing/item001{"sku":"item001","price":179.99}
$ curl -X DELETE https://api.example.com/api/warehouse/pricing/item001
{"status":405,"message":"Method not allowed"}Anwenden einer fein abgestuften Zugriffskontrolle
Teil 1 dieser Reihe beschrieb, wie sich APIs vor unbefugtem Zugriff schützen lassen, indem Authentifizierungsoptionen wie API-Schlüssel und JSON Web Tokens (JWTs) aktiviert werden. Wir können die authentifizierte ID oder deren Attribute verwenden, um eine differenzierte Zugriffskontrolle umzusetzen.
Im Folgenden zeigen wir zwei solcher Beispiele.
- Das erste Beispiel steuert den Zugriff auf eine bestimmte API-Ressource, erweitert eine in Teil 1 vorgestellte Konfiguration und verwendet die API-Schlüsselauthentifizierung, um zu überprüfen, ob ein bestimmter API-Client auf der Zulassungsliste steht.
- Das zweite Beispiel legt fest, welche HTTP-Methoden ein Client verwenden darf. Es implementiert die in Teil 1 erwähnte JWT-Authentifizierungsmethode, wobei ein benutzerdefinierter Claim verwendet wird, um berechtigte API-Clients zu identifizieren. (Beachten Sie, dass JWT-Unterstützung ausschließlich in NGINX Plus verfügbar ist.)
Natürlich können für diese beispielhaften Anwendungsfälle auch andere Authentifizierungsmethoden verwendet werden, beispielsweise die HTTP-Basisauthentifizierung und die OAuth-2.0-Token-Introspektion.
Zugriff auf bestimmte Ressourcen steuern
Angenommen, wir möchten nur „Infrastruktur-Clients“ den Zugriff auf die Ressource audit des Bestandsdienstes der Warehouse API erlauben. Bei aktivierter API-Schlüssel-Authentifizierung verwenden wir einen map-Block, um eine Positivliste mit den Namen der Infrastruktur-Clients zu erstellen, sodass die Variable $is_infrastructure den Wert 1 annimmt, wenn ein entsprechender API-Schlüssel verwendet wird.
Codebeispiel wird geladen…
In der Definition der Warehouse-API fügen wir einen location-Block für die Ressource zur Inventarprüfung hinzu (Zeilen 15–20). Der if-Block stellt sicher, dass nur Infrastruktur-Clients auf die Ressource zugreifen können.
Codebeispiel wird geladen…
Beachten Sie, dass die location-Direktive in Zeile 15 den Modifikator = (Gleichheitszeichen) verwendet, um eine exakte Übereinstimmung mit der Ressource audit zu erzielen. Exakte Übereinstimmungen haben Vorrang vor den standardmäßigen Pfadpräfixdefinitionen, die für die anderen Ressourcen verwendet werden. Der folgende Trace zeigt, dass ein Client, der nicht auf der Zulassungsliste steht, bei dieser Konfiguration nicht auf die Ressource audit des Inventars zugreifen kann. Der angezeigte API-Schlüssel gehört zu client_two (wie in Teil 1 definiert).
$ curl -H "apikey: QzVV6y1EmQFbbxOfRCwyJs35" https://api.example.com/api/warehouse/inventory/audit{"status":403,"message":"Forbidden"}Zugriff auf bestimmte Methoden steuern
Wie oben beschrieben, akzeptiert der Preisdienst die Methoden GET und PATCH, mit denen Clients den Preis eines bestimmten Artikels abrufen beziehungsweise ändern können. (Wir könnten auch die Methoden POST und DELETE zulassen, um die Preisdaten über ihren gesamten Lebenszyklus hinweg verwalten zu können.) In diesem Abschnitt erweitern wir diesen Anwendungsfall, um zu steuern, welche Methoden bestimmte Benutzer aufrufen dürfen. Wenn die JWT-Authentifizierung für die Warehouse API aktiviert ist, werden die Berechtigungen jedes Clients als benutzerdefinierte Claims codiert. Die JWTs, die Administratoren mit der Berechtigung zum Ändern von Preisdaten ausgestellt werden, enthalten den Claim "admin":true. Nun erweitern wir unsere Zugriffssteuerungslogik so, dass nur Administratoren Änderungen vornehmen können.
Codebeispiel wird geladen…
Dieser map-Block, der am Ende von api_gateway.conf hinzugefügt wird, verwendet die Anfragemethode ($request_method) als Eingabe und erzeugt eine neue Variable: $admin_permitted_method. Schreibgeschützte Methoden sind immer zulässig (Zeilen 62–64), der Zugriff auf Schreibvorgänge hängt jedoch vom Wert des admin-Claims im JWT ab (Zeile 65). Nun erweitern wir die Konfiguration unserer Warehouse-API, um sicherzustellen, dass nur Administratoren Preisänderungen vornehmen können.
Codebeispiel wird geladen…
Die Warehouse-API verlangt von allen Clients die Vorlage eines gültigen JWT (Zeile 7). Außerdem prüfen wir durch Auswertung der Variable „$admin_permitted_method“ (Zeile 25), ob Schreibvorgänge zulässig sind. Beachten Sie erneut, dass die JWT-Authentifizierung ausschließlich in NGINX Plus verfügbar ist.
Anfragegrößen begrenzen
HTTP-APIs verwenden üblicherweise den Anfragetext, um Anweisungen und Daten zu übermitteln, die vom Backend-API-Dienst verarbeitet werden sollen. Dies gilt sowohl für XML-/SOAP-APIs als auch für JSON-/REST-APIs. Daher kann der Anfragetext einen Angriffsvektor für die Backend-API-Dienste darstellen, die bei der Verarbeitung sehr großer Anfragetexte möglicherweise anfällig für Pufferüberlauf-Angriffe sind.
Standardmäßig lehnt NGINX Anfragen ab, deren Body größer als 1 MB ist. Dieser Grenzwert kann für APIs erhöht werden, die speziell große Nutzdaten verarbeiten, beispielsweise bei der Bildverarbeitung. Für die meisten APIs legen wir jedoch einen niedrigeren Wert fest.
Codebeispiel wird geladen…
Die client_max_body_size-Direktive in Zeile 7 begrenzt die Größe des Anfragekörpers. Mit dieser Konfiguration können wir das Verhalten des API-Gateways beim Empfang zweier unterschiedlicher PATCH-Anfragen an den Preisdienst vergleichen. Der erste curl-Befehl sendet eine kleine Menge JSON-Daten, während der zweite Befehl versucht, den Inhalt einer großen Datei zu senden (/etc/services).
$ curl -iX PATCH -d '{"price":199.99}' https://api.example.com/api/warehouse/pricing/item001HTTP/1.1 204 No Content
Server: nginx/1.19.5
Connection: keep-alive
$ curl -iX PATCH -d@/etc/services https://api.example.com/api/warehouse/pricing/item001
HTTP/1.1 413 Request Entity Too Large
Server: nginx/1.19.5
Content-Type: application/json
Content-Length: 45
Connection: close
{"status":413,"message":"Payload too large"}Anfrageinhalte validieren
[Redaktion – Der folgende Anwendungsfall ist einer von mehreren für das NGINX-JavaScript-Modul. Eine vollständige Liste finden Sie unter „Anwendungsfälle für das NGINX-JavaScript-Modul“. ]
Backend-API-Dienste sind nicht nur anfällig für Pufferüberlaufangriffe durch große Anfragetexte, sondern auch für Anfragetexte mit ungültigen oder unerwarteten Daten. Bei Anwendungen, die korrekt formatiertes JSON im Anfragetext erfordern, können wir das NGINX JavaScript module<.htmla> verwenden, um vor der Weiterleitung an den Backend-API-Dienst zu überprüfen, ob die JSON-Daten fehlerfrei geparst werden.
Wenn das JavaScript-Modul installiert ist, verwenden wir die Direktive js_import, um auf die Datei mit dem JavaScript-Code für die Funktion zu verweisen, die JSON-Daten validiert.
Codebeispiel wird geladen…
Die js_set-Direktive definiert eine neue Variable, $json_validated, die durch Aufrufen der Funktion parseRequestBody ausgewertet wird.
Codebeispiel wird geladen…
Die Funktion parseRequestBody versucht, den Anfragetext mit der Methode JSON.parse zu parsen (Zeile 6). Wenn das Parsen erfolgreich ist, wird der Name der für diese Anfrage vorgesehenen Upstream-Gruppe zurückgegeben (Zeile 8). Wenn der Anfragetext nicht geparst werden kann (wodurch eine Ausnahme ausgelöst wird), wird eine lokale Serveradresse zurückgegeben (Zeile 11). Die Direktive return weist der Variablen $json_validated einen Wert zu, anhand dessen wir bestimmen können, wohin die Anfrage gesendet werden soll.
Codebeispiel wird geladen…
Im Abschnitt zum URI-Routing der Warehouse API ändern wir die proxy_pass-Direktive in Zeile 22. Sie leitet die Anfrage wie bei den in den vorherigen Abschnitten beschriebenen Warehouse-API-Konfigurationen an den Backend-API-Dienst weiter, verwendet nun jedoch die Variable $json_validated als Zieladresse. Wenn der Client-Body erfolgreich als JSON geparst wurde, leiten wir die Anfrage an die in Zeile 15 definierte Upstream-Gruppe weiter. Ist hingegen eine Ausnahme aufgetreten, verwenden wir den zurückgegebenen Wert 127.0.0.1:10415, um eine Fehlerantwort an den Client zu senden.
Codebeispiel wird geladen…
Wenn Anfragen an diesen virtuellen Server weitergeleitet werden, sendet NGINX die Antwort 415 (Nicht unterstützter Medientyp) an den Client.
Mit dieser vollständigen Konfiguration leitet NGINX Anfragen nur dann an den Backend-API-Dienst weiter, wenn sie korrekt formatierte JSON-Anfragetexte enthalten.
$ curl -iX POST -d '{"sku":"item002","price":85.00}' https://api.example.com/api/warehouse/pricingHTTP/1.1 201 Created
Server: nginx/1.19.5
Location: /api/warehouse/pricing/item002
$ curl -X POST -d 'item002=85.00' https://api.example.com/api/warehouse/pricing
{"status":415,"message":"Unsupported media type"}Ein Hinweis zur Variable „$request_body“
Die JavaScript-Funktion parseRequestBody verwendet die Variable $request_body, um JSON zu parsen. NGINX befüllt diese Variable jedoch standardmäßig nicht, sondern streamt den Request-Body einfach an das Backend, ohne Zwischenkopien zu erstellen. Durch die Verwendung der Direktive mirror im Abschnitt für das URI-Routing (Zeile 16) erstellen wir eine Kopie der Client-Anfrage und befüllen dadurch die Variable $request_body.
Codebeispiel wird geladen…
Die Direktiven in den Zeilen 17 und 19 steuern, wie NGINX den Anfragetext intern verarbeitet. Wir setzen client_body_buffer_size auf denselben Wert wie client_max_body_size, damit der Anfragetext nicht auf die Festplatte geschrieben wird. Dies verbessert die Gesamtleistung, da Festplatten-E/A-Vorgänge minimiert werden, allerdings auf Kosten einer höheren Speicherauslastung. Für die meisten API-Gateway-Anwendungsfälle mit kleinen Anfragetexten ist dies ein guter Kompromiss.
Wie bereits erwähnt, erstellt die mirror-Direktive eine Kopie der Clientanfrage. Abgesehen davon, $request_body zu befüllen, benötigen wir diese Kopie nicht. Daher senden wir sie an einen „Sackgassen“-Speicherort (/_get_request_body), den wir im server-Block der übergeordneten API-Gateway-Konfiguration definieren.
Codebeispiel wird geladen…
Dieser Location-Block sendet lediglich die Antwort 204 (No Content). Da diese Antwort zu einer gespiegelten Anfrage gehört, wird sie ignoriert und verursacht daher bei der Verarbeitung der ursprünglichen Client-Anfrage nur einen vernachlässigbaren Mehraufwand.
Zusammenfassung
In diesem zweiten Blogbeitrag unserer Reihe über die Bereitstellung von NGINX Open Source und NGINX Plus als API-Gateway haben wir uns mit der Herausforderung befasst, Backend-API-Dienste in einer Produktionsumgebung vor böswilligen Clients und solchen mit Fehlverhalten zu schützen. NGINX nutzt für die Verwaltung des API-Datenverkehrs dieselbe Technologie, mit der auch die meistbesuchten Websites im heutigen Internet betrieben und geschützt werden.
Lesen Sie auch den anderen Beitrag dieser Reihe:
- Teil 1 erläutert, wie NGINX für einige grundlegende API-Gateway-Anwendungsfälle konfiguriert wird.
- Teil 3 erläutert, wie NGINX als API-Gateway für gRPC-Dienste bereitgestellt wird.
Wenn Sie NGINX Plus als API-Gateway testen möchten, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen. Verwenden Sie während Ihres Testzeitraums den vollständigen Satz an Konfigurationsdateien aus unserem GitHub-Gist-Repository.
Über den Autor

Ähnliche Blogbeiträge
Die Zukunft von NGINX: Zurück zu unseren Open-Source-Wurzeln
Beim NGINX Sprint 2022 besinnen wir uns auf unsere Open-Source-Wurzeln. Erfahren Sie mehr über die Initiativen, die wir im kommenden Jahr verfolgen, und teilen Sie uns mit, was Sie sich wünschen.

Ein Leitfaden zur Auswahl eines Ingress-Controllers, Teil 1: Ermitteln Sie Ihre Anforderungen
Ein Ingress-Controller kann eines der leistungsstärksten Tools in Ihrem Kubernetes-Stack sein. Erfahren Sie, wie Sie Ihre Ingress-Anforderungen ermitteln, um die beste Option auszuwählen.
Bereitstellung des NGINX Ingress Controllers auf Amazon EKS: So haben wir getestet
Wir haben kürzlich den Lösungsleitfaden zum NGINX Ingress Controller um Empfehlungen zur Dimensionierung für Amazon Elastic Kubernetes Service (EKS) ergänzt. In diesem Blogbeitrag erläutern wir, wie wir diese Empfehlungen erarbeitet haben, und stellen Ihnen alle Informationen zur Verfügung, die Sie benötigen, um selbst ähnliche Tests durchzuführen.

Protokollierung des Datenverkehrs zwischen NGINX und Upstream-Servern bei CDN77
Bei der Fehlersuche bei Datenverkehrsproblemen zwischen NGINX und Upstream-Servern konnte CDN77 die benötigten Daten nur aus dem Fehlerprotokoll mit aktivierter Debug-Ausgabe beziehen, was die Leistung beeinträchtigt und riesige Dateien erzeugt. Daher entwickelte das Team ein neues benutzerdefiniertes Modul, das ausschließlich die benötigten Daten protokolliert.
Ankündigung von NGINX Plus R16
NGINX Plus R16 ist eine einheitliche, elastische Ingress- und Egress-Schicht für Anwendungen, die die Funktionen eines Load Balancers, API-Gateways und einer WAF vereint und neue Features wie clusterfähige Ratenbegrenzung, Schlüssel-Wert-Speicher, „Random with Two Choices“, verbessertes UDP-Load-Balancing und vieles mehr bietet.
5 Gründe für den Umstieg auf softwarebasiertes Load Balancing
Entdecken Sie fünf Gründe für den Wechsel zu einem softwarebasierten Load Balancer wie NGINX Plus, darunter Kosteneinsparungen, die Einführung von DevOps und eine einheitliche Lösung für alle Umgebungen.