[Lesezeichen] 23 Empfehlungen zum Schutz von Node.js-Anwendungen

Heutzutage sind Webdienste ständig einer Vielzahl von Angriffen ausgesetzt. Daher ist Sicherheit in allen Phasen des Projektlebenszyklus zu beachten. Die Autoren des Materials, dessen Übersetzung wir heute veröffentlichen, unterhalten auf GitHub ein Repository mit etwa 80 Empfehlungen zum Sichern von Anwendungen, die auf der Node.js-Plattform ausgeführt werden. Dieses Material, das auf vielen Sicherheitspublikationen basiert, enthält mehr als zwei Dutzend Empfehlungen zu Node.js und einige allgemeine Tipps. Darüber hinaus werden in diesem Material die zehn wichtigsten Sicherheitslücken aus der Liste des OWASP-Projekts behandelt.



1. Verwenden Sie die Regeln für den Linter, um die Sicherheit des Codes zu überprüfen



Empfehlungen


Verwenden Sie während der Entwicklung ein sicherheitsorientiertes Plug-In für den Linter, z. B. eslint-plugin-security . Auf diese Weise können Sie Schwachstellen und Sicherheitsprobleme sehr früh erkennen - zum Zeitpunkt des Schreibens des entsprechenden Codes. Dieser Ansatz hilft, Schwachstellen in der Programmsicherheit zu finden. Dazu gehören die Verwendung des Befehls eval , der Aufruf untergeordneter Prozesse, der Import von Modulen mit der Übertragung von etwas, das sich von einem Zeichenfolgenliteral unterscheidet, auf den entsprechenden Befehl (z. B. eine bestimmte Zeichenfolge, die auf der Grundlage von Daten erstellt wird, die vom Benutzer an den Server übertragen werden).

Hier finden Sie nützliches Material zu Linter-Regeln

Adam Baldwin sagt Folgendes über Flusen: „Linter sollte nicht nur ein Werkzeug sein, das die Regeln bezüglich der Anzahl der verwendeten Leerzeichen, Semikolons und der Verwendung des Befehls eval genau befolgt. ESLint bietet dem Entwickler eine leistungsstarke Plattform, mit der eine Vielzahl potenziell gefährlicher Muster im Code erkannt und beseitigt werden können. Wir sprechen zum Beispiel über reguläre Ausdrücke, über das Überprüfen von Benutzereingaben und so weiter. Ich glaube, dass der Linter Entwicklern, die sich mit Sicherheitsproblemen befassen, ein leistungsstarkes neues Tool bietet, auf das es sich zu achten lohnt. “

Mögliche Probleme


Was während der Entwicklung wie eine kleine Sicherheitslücke aussieht, wird zu einer ernsthaften Sicherheitslücke in der Produktion. Wenn nicht alle Projektentwickler bei der Arbeit mit Code einheitliche Sicherheitsregeln befolgen, kann dies zu Sicherheitslücken oder beispielsweise zum Eindringen vertraulicher Daten in öffentliche Repositorys führen.

2. Begrenzen Sie die Anzahl gleichzeitiger Serveranforderungen



Empfehlungen


DoS-Angriffe sind bei Cyberkriminellen sehr beliebt und relativ einfach durchzuführen. Sie können ein System implementieren, um die Anzahl der Anforderungen an eine Anwendung mithilfe eines externen Dienstes zu begrenzen, z. B. einen Cloud Load Balancer, eine Cloud-Firewall, einen Nginx-Server, oder bei kleinen Anwendungen, die nicht kritisch sind, Middleware verwenden, um die Anzahl der Anforderungen wie Expressrate zu begrenzen -limit .

Hier finden Sie Informationen zur Implementierung des Systems zur Begrenzung der Häufigkeit von Anforderungen an den Server

Mögliche Probleme


Eine Anwendung, die kein System zur Begrenzung der Anzahl gleichzeitiger Anforderungen bietet, kann angegriffen werden, was zu ihrem Ausfall führt. Dies drückt sich darin aus, dass Benutzer einer solchen Anwendung entweder Schwierigkeiten haben, damit zu arbeiten, oder überhaupt nicht in der Lage sind, mit ihr zu interagieren.

3. Entfernen Sie vertrauliche Informationen aus Konfigurationsdateien oder verschlüsseln Sie sie



Empfehlungen


Speichern Sie vertrauliche Daten niemals im Klartext in Konfigurationsdateien oder im Code. Verwenden Sie stattdessen vertrauliche Datenverwaltungssysteme wie Vault-Produkte oder Kubernetes / Docker Secrets-Systeme oder verwenden Sie Umgebungsvariablen zum Speichern solcher Daten. Im Versionskontrollsystem gespeicherte vertrauliche Daten müssen verschlüsselt sein, und es müssen Maßnahmen zu ihrer sicheren Speicherung und Verwendung getroffen werden. Zu diesen Maßnahmen gehören die Verwendung dynamischer Schlüssel, die Verwendung von Kennwortablaufdaten, Sicherheitsüberprüfungen usw. Verwenden Sie Systeme, um den Code vor dem Festschreiben oder vor dem Senden an das Repository zu überprüfen, um ein versehentliches Senden vertraulicher Daten an das öffentliche Repository zu verhindern.

Hier finden Sie Informationen zur Verwaltung vertraulicher Daten

Mögliche Probleme


Selbst wenn der Code eines Tages versehentlich in einem geschlossenen Repository gespeichert wird, wird er möglicherweise öffentlich verfügbar. Zu diesem Zeitpunkt werden alle darin gespeicherten vertraulichen Daten gemeinfrei. Infolgedessen führt der Zugriff von Drittanbietern auf das Repository mit dem Code versehentlich dazu, dass dieser Zugriff auf die damit verbundenen Systeme (Datenbanken, APIs, Dienste usw.) erhält.

4. Verhindern Sie Schwachstellen bei der Codeinjektion



Empfehlungen


Um SQL / NoSQL-Injektionen und ähnliche Angriffe zu verhindern, verwenden Sie immer ORM / ODM-Bibliotheken oder DBMS-Mechanismen, die auf die Datenbereinigung abzielen oder benannte oder indizierte parametrisierte Abfragen unterstützen und überprüfen, was vom Benutzer kommt. Verwenden Sie zum Einbetten bestimmter Werte in Abfragetexte niemals nur Vorlagen-JavaScript-Zeichenfolgen oder Zeichenfolgenverkettungen, da dieser Ansatz Ihre Anwendung für eine Vielzahl von Sicherheitslücken öffnet. Alle seriösen Bibliotheken für Node.js, die für die Arbeit mit Daten verwendet werden (z. B. Sequelize , Knex , Mungo ), enthalten einen integrierten Schutz vor Angriffen durch Einfügen von Code.

Hier ist das Material zur Injektionsprävention mit ORM / ODM-Bibliotheken

Mögliche Probleme


Die Verwendung nicht verifizierter und nicht gereinigter Daten, die vom Benutzer in Abfragen empfangen werden, kann zu einem Angriff führen, indem ein Operator eingeführt wird, wenn mit einer NoSQL-Datenbank wie MongoDB gearbeitet wird. Wenn Sie beim Arbeiten mit einer SQL-Datenbank kein Datenbereinigungssystem oder keine ORM-Bibliothek verwenden, besteht die Möglichkeit eines Angriffs durch SQL-Injection, wodurch eine große Sicherheitslücke in der Anwendung entsteht.

5. Vermeiden Sie DoS-Angriffe, indem Sie explizit die Bedingungen für eine abnormale Beendigung des Prozesses festlegen


Empfehlungen


Der Knotenprozess stürzt ab, wenn ein nicht behandelter Fehler auftritt. In vielen Empfehlungen, die Best Practices für Node widerspiegeln, wird jedoch empfohlen, Prozesse zu beenden, selbst wenn ein Fehler abgefangen und verarbeitet wurde. Express stürzt beispielsweise ab, wenn ein asynchroner Fehler auftritt - es sei denn, die Routen sind in catch Ausdrücke eingeschlossen. Diese Tatsache bietet Angreifern eine sehr attraktive Gelegenheit. Nachdem sie festgestellt haben, dass der Prozess abstürzt, wenn eine bestimmte Anforderung eintrifft, senden sie genau diese Anforderungen an sie. Es gibt keine Empfehlung, die dieses Problem auf einen Schlag lösen würde. Einige Tricks können es jedoch mildern. Am Ende des Prozesses müssen Sie den Administrator aufgrund eines nicht behandelten Fehlers benachrichtigen, wobei einer solchen Benachrichtigung die höchste Priorität eingeräumt wird. Es ist erforderlich zu überprüfen, was in Anforderungen zum Prozess kommt, und Situationen zu vermeiden, in denen der Prozess aufgrund von Anforderungen, die versehentlich oder absichtlich falsch gebildet wurden, abnormal beendet wird. Alle Routen müssen in catch Ausdrücke eingeschlossen sein, und das System muss so konfiguriert sein, dass der Prozess nicht abstürzt, wenn die Anforderung die Fehlerursache ist (im Gegensatz zu dem, was auf globaler Anwendungsebene geschieht).

Mögliche Probleme


Lassen Sie uns die folgende Situation analysieren. Es gibt viele Node.js-Anwendungen. Was passiert, wenn wir POST-Anforderungen mit leerem JSON als Anforderungshauptteil senden? Dies führt zum Absturz vieler dieser Anwendungen.

Wenn wir nun die Rolle von Cyberkriminellen spielen würden, um zu verhindern, dass die Anwendungen abstürzen, würde es ausreichen, ihnen weiterhin ähnliche Anfragen zu senden.

6. Konfigurieren Sie HTTP-Antwortheader, um die Projektsicherheit zu erhöhen



Empfehlungen


Eine Anwendung sollte sicherheitsorientierte HTTP-Header verwenden, um zu verhindern, dass Angreifer auf gängige Angriffstechniken wie Cross-Site-Scripting (XSS), Clickjacking und andere zurückgreifen. Das Anpassen der Header ist mit speziellen Modulen wie Helm einfach.

Hier finden Sie Informationen zur Verwendung sicherer Header

Mögliche Probleme


Wenn keine sicheren HTTP-Header verwendet werden, können Angreifer Angriffe auf Benutzer Ihrer Anwendungen ausführen, was zu großen Sicherheitslücken führt.

7. Überprüfen Sie Ihre Projekte kontinuierlich und automatisch auf die Verwendung anfälliger Abhängigkeiten



Empfehlungen


Im NPM-Ökosystem sind Projekte mit vielen Abhängigkeiten sehr verbreitet. Abhängigkeiten sollten immer kontrolliert werden, wenn neue Schwachstellen entdeckt werden. Verwenden Sie Tools wie npm audit , nsp oder snyk , um anfällige Abhängigkeiten zu erkennen, zu überwachen und zu beheben. Betten Sie diese Tools in Ihr kontinuierliches Integrationssystem ein. Auf diese Weise können Sie anfällige Abhängigkeiten erkennen, bevor sie in Produktion gehen.

Hier finden Sie Informationen zur Sicherheit von Projektabhängigkeiten

Mögliche Probleme


Ein Angreifer kann das im Projekt verwendete Webframework bestimmen und Angriffe auf alle bekannten Sicherheitslücken ausführen.

8. Versuchen Sie, das Standard-Kryptomodul Node.js nicht für die Kennwortverarbeitung zu verwenden. Verwenden Sie stattdessen Bcrypt



Empfehlungen


Passwörter oder andere vertrauliche Daten (z. B. API-Schlüssel) müssen gespeichert werden, indem sie mit kryptografischen Funktionen unter Verwendung von "salt" wie Bcrypt verarbeitet werden. Aus Sicherheits- und Leistungsgründen lohnt es sich, genau etwas Ähnliches zu verwenden und nicht das Standard- crypto Node.js.

Hier ist das Material zu Bcrypt

Mögliche Probleme


Passwörter oder einige vertrauliche Daten, die ohne geeignete Schutzmaßnahmen gespeichert werden, sind anfällig für Brute-Force-Angriffe und Wörterbuchangriffe, die zur Offenlegung dieser Daten führen.

9. Verwenden Sie Zeichen-Escape-Systeme in HTML-, JS- und CSS-Daten, die an den Benutzer gesendet werden



Empfehlungen


Wenn einige Daten von einer nicht vertrauenswürdigen Quelle an den Browser des Benutzers gesendet werden, können diese Daten auch dann ein Code sein, der ausgeführt werden kann, wenn sie einfach angezeigt werden sollen. Dies wird allgemein als Cross-Site-Scripting (XSS) bezeichnet. Sie können das Risiko verringern, dass solche Angriffe möglich sind, indem Sie spezielle Bibliotheken verwenden, die Daten so verarbeiten, dass sie nicht ausgeführt werden können. Dies wird als Codierungs- oder Abschirmungsdaten bezeichnet.

Hier finden Sie Informationen zur Abschirmung des Ausgangs

Mögliche Probleme


Wenn Sie sich nicht für die Datenabschirmung interessieren, kann ein Angreifer beispielsweise schädlichen JavaScript-Code in Ihrer Datenbank speichern, der unverändert an Clients übertragen und gestartet werden kann.

10. Überprüfen Sie die JSON-Daten, die auf den Server gelangen



Empfehlungen


Kontrollieren Sie den Inhalt der Textkörper eingehender Anforderungen und prüfen Sie, ob sie dem entsprechen, was Sie in solchen Anforderungen erwarten. Wenn die Anforderung nicht wie erwartet aussieht, beenden Sie die Verarbeitung schnell. Um den zeitaufwändigen Vorgang des Schreibens eines Anforderungsüberprüfungscodes für jede Route zu vermeiden, können Sie einfache JSON-Tools für die Datenvalidierung verwenden, z. B. jsonschema oder joi .

Hier finden Sie das Material zum Überprüfen eingehender JSON-Daten

Mögliche Probleme


Wenn der Server Anfragen herzlich akzeptiert, ohne sie gründlich zu prüfen, erhöht dies die Angriffsfläche der Anwendung erheblich und inspiriert Angreifer dazu, viele von ihnen zu finden, die zu einem „Absturz“ des Systems führen.

11. Pflegen Sie JWT-Token auf der schwarzen Liste



Empfehlungen


Bei der Verwendung von JWT-Token (z. B. wenn Sie mit Passport.js arbeiten ) gibt es standardmäßig keinen Standardmechanismus zum Widerrufen von Systemzugriffsberechtigungen für bereits ausgestellte Token. Selbst wenn Sie feststellen, dass ein bestimmter Benutzer etwas offensichtlich Anormales tut, haben Sie über den Token-Mechanismus keine Möglichkeit, seinen Zugriff auf das System zu blockieren, solange er über ein gültiges Token verfügt. Dieses Problem kann durch die Implementierung einer schwarzen Liste nicht vertrauenswürdiger Token behoben werden, deren Validierung bei jeder Anforderung durchgeführt wird.

Hier ist das Material zu JWT-Token auf der schwarzen Liste

Mögliche Probleme


Token, die in die falschen Hände geraten, können von einem Angreifer verwendet werden. Er kann auf die Anwendung zugreifen und sich als normaler Benutzer ausgeben - der Eigentümer des Tokens.

12. Begrenzen Sie die Anzahl der Anmeldeversuche



Empfehlungen


In Anwendungen, die auf Express basieren, lohnt es sich zum Schutz vor Brute-Force-Angriffen und Wörterbuchangriffen, die entsprechende Middleware wie Express-Brute zu verwenden . Ebenso müssen Sie kritische Routen wie /admin oder /login schützen. Der Schutz sollte auf der Analyse von Abfrageeigenschaften basieren, z. B. dem in der Abfrage verwendeten Benutzernamen oder anderen Bezeichnern, z. B. Abfragekörperparametern.

Hier finden Sie Informationen zur Begrenzung der Anzahl der Anmeldeversuche

Mögliche Probleme


Wenn die Anwendung die Anzahl der Anmeldeversuche nicht begrenzt, kann der Angreifer automatisch eine unbegrenzte Anzahl von Anmeldeanforderungen an Ihr System senden, z. B. um Zugriff auf ein privilegiertes Konto zu erhalten.

13. Führen Sie Node.js als Nicht-Root-Benutzer aus



Empfehlungen


Ein äußerst häufiges Szenario ist, wenn Node.js als Root-Benutzer mit unbegrenzten Berechtigungen ausgeführt wird. So wird beispielsweise in Docker-Containern standardmäßig alles konfiguriert. Es wird empfohlen, einen Benutzer ohne Root-Rechte zu erstellen und ihn entweder in das Docker-Image einzubetten oder den Vorgang im Namen dieses Benutzers zu starten, indem Sie den Container mit dem Flag -u username aufrufen.

Hier ist das Material zum Starten von Node.js als Nicht-Root-Benutzer

Mögliche Probleme


Wenn Node.js unter dem Root-Benutzerkonto ausgeführt wird, erhält ein Angreifer, der ein Skript auf dem Server ausführen konnte, unbegrenzte Möglichkeiten auf dem lokalen Computer. iptable , es kann iptable Einstellungen ändern und den Datenverkehr auf seinen eigenen Computer umleiten.

14. Begrenzen Sie die Datenmenge, die in Anforderungen mit einem Reverse-Proxy-Server oder einer Middleware übertragen wird



Empfehlungen


Je größer die Datenmenge im Anforderungshauptteil ist, desto schwieriger ist es für einen Single-Threaded-Server, eine solche Anforderung zu verarbeiten. Die Verwendung großer Anforderungen gibt einem Angreifer die Möglichkeit, den Server mit unnötiger Arbeit zu überfluten, ohne ihm eine große Anzahl von Anforderungen zu senden (dh ohne einen DoS / DDoS-Angriff auszuführen). Sie können das Risiko solcher Angriffe verringern, indem Sie die Größe der eingehenden Anforderungen auf einem bestimmten Grenzsystem (auf einer Firewall oder einem Load Balancer) begrenzen oder den Body-Parser- Express so einstellen, dass nur Pakete mit einer geringen Datenmenge empfangen werden.

Hier finden Sie Informationen zur Begrenzung der Datenmenge, die in Anforderungen übertragen wird

Mögliche Probleme


Wenn Sie die in Anforderungen übertragene Datenmenge nicht begrenzen, kann ein Angreifer die Anwendung laden, indem er große Anforderungen verarbeitet. Derzeit ist es nicht in der Lage, die Aufgaben zu lösen, für die es entwickelt wurde. Dies führt zu einer schlechten Leistung und macht die Anwendung anfällig für DoS-Angriffe.

15. Vermeiden Sie die Verwendung der Eval-Funktion in JavaScript



Empfehlungen


Die eval Funktion ist böse, weil Sie damit beliebigen JS-Code ausführen können, der während der Programmausführung an sie übergeben wird. Darüber hinaus kann dieser Code bei weitem nicht nur die Anwendung verlangsamen. Diese Funktion stellt ein ernstes Sicherheitsrisiko dar, da böswilliger JS-Code, der von einem Angreifer an den Server gesendet wird, in diesen eindringen kann.

Außerdem sollten Sie den new Function vermeiden. Die Funktionen setTimeout und setInterval müssen niemals dynamisch generierten JS-Code übergeben.

Hier ist das Zeug über Eval

Mögliche Probleme


Wenn jemand einen Weg findet, schädlichen JS-Code in Form von Text, einer eval oder einer anderen ähnlichen JS-Engine zu übertragen, hat er vollen Zugriff auf die Seite und auf alles, was mit JavaScript möglich ist. Diese Sicherheitsanfälligkeit ist häufig mit XSS-Angriffen verbunden.

16. Überladen Sie Node.js-Anwendungen mit einem Thread nicht mit schädlichen regulären Ausdrücken



Empfehlungen


Obwohl reguläre Ausdrücke praktisch sind, stellen sie eine Bedrohung für JavaScript-Anwendungen im Allgemeinen und im Besonderen für die Node.js.-Plattform dar. Die Verarbeitung mit regulären Ausdrücken, die vom Benutzer stammen, kann den Prozessor stark belasten. Beispielsweise kann die Verarbeitung regulärer Ausdrücke so ineffizient sein, dass das Überprüfen von zehn Wörtern den Ereigniszyklus für mehrere Sekunden blockieren und den Prozessor überlasten kann. Daher ist es am besten, Pakete von Drittanbietern zum Überprüfen von Zeichenfolgendaten wie validator.js zu verwenden , anstatt Ihre eigenen regulären Ausdrücke zu schreiben. Sie können das Safe-Regex- Paket verwenden, um anfällige Regex- Muster zu erkennen.

Hier ist das Anti-Malware-Regex-Material

Mögliche Probleme


Schlecht geschriebene reguläre Ausdrücke können einer besonderen Art von DoS-Angriffen ausgesetzt sein, bei denen die Ereignisschleife vollständig blockiert ist. Beispielsweise wurde im November 2017 festgestellt, dass das beliebte moment Paket für solche Angriffe anfällig ist.

17. Vermeiden Sie das Laden von Modulen mithilfe von Variablen



Empfehlungen


Vermeiden Sie den Import von Dateien, deren Pfad als Parameter angegeben ist, basierend auf Überlegungen, nach denen dieser Parameter basierend auf Daten des Benutzers festgelegt werden kann. Diese Regel kann erweitert werden, um hier den Zugriff auf Dateien (mithilfe von fs.readFile() ) und den Zugriff auf andere wichtige Ressourcen mithilfe der vom Benutzer empfangenen Parameter fs.readFile() . Die Verwendung von eslint-plugin-security ermöglicht es, solche unsicheren Muster sehr früh zu erkennen.

Hier finden Sie Informationen zum sicheren Laden von Modulen

Mögliche Probleme


Die von einem Angreifer an den Server gesendeten Daten befinden sich möglicherweise in den Einstellungen, die für den Import bestimmter Dateien verantwortlich sind, darunter möglicherweise eine schädliche Datei, die zuvor auf den Server hochgeladen wurde. Diese Daten können auch verwendet werden, um auf Systemdateien zuzugreifen, die sich bereits auf dem Computer befinden.

18.




, (, ), «», . , ( cluster.fork() ), npm-, .




, , . — , , , .

19.




, , . , , , . child_process.execFile , , , , , .

,


, , , , .

20.




express, , . , , , Error ( ). , , , - , .




, , , , , , .

21. npm Yarn




, -, (MFA, multi-factor authentication). npm Yarn , . , , , . , . , , npm, .


, ? eslint, .

22. ,




- . , — , - . , , X-Powered-By , , . , ( , , Node.js express).

-


- . , , -, — . .

23.


, . — , Node.js. , OWASP .

  • root- .
  • ( SSH-).
  • , , , . OWASP, .
  • , . , .
  • OAuth, OpenID, . Basic Authentication.
  • . , ( — ), X , Y .
  • — , — . , .
  • , , . ( — GitHub, AWS, Jenkins, ).

Zusammenfassung


. , Node.js-.

Liebe Leser! -, ?

Source: https://habr.com/ru/post/de419719/


All Articles