Aus Linux-Magazin 09/2020

Javascript-Runtime Deno mit Fokus auf Sicherheit

© Andrey Kiselev, 123RF

Die neue Javascript-Laufzeitumgebung Deno macht dem Platzhirsch Node.js mit einigen interessanten Konzepten Konkurrenz. Nach zwei Jahren Entwicklung liegt sie seit Mai 2020 in einer stabilen Version vor.

Ryan Dahl holte 2009 die Skriptsprache Javascript aus dem Browser auf die Kommandozeile. Die von ihm entwickelte Software Node.js [1] führt dort direkt Javascript-Programme aus. Zwar mauserte sich die Laufzeitumgebung zu einem weltweiten Erfolgsmodell, ihr Schöpfer zeigte sich jedoch zunehmend unzufrieden über einige seiner ursprünglichen Designentscheidungen. Genau diese stellte er 2018 in einem Vortrag auf der Javascript-Konferenz JSConf an den Pranger [2] und kündigte zugleich das komplett neu implementierte Deno [3] an. Dessen Name ist ein Anagramm aus den Buchstaben des Worts Node.

Andersartig

Deno führt auf der Kommandozeile Javascript-Programme aus. Den Code verarbeitet dabei unter der Haube die recht flotte V8-Engine, die unter anderem auch in Googles Browser Chrome und in Node.js steckt. Im Gegensatz zu seinem Konkurrenten verdaut Deno neben ECMAScript ES6 von Haus aus auch Typescript. Alle asynchronen Aktionen liefern zudem immer ein Promise zurück.

Deno selbst ist komplett in Rust geschrieben, was die Laufzeitumgebung unter anderem dank der in der Sprache eingebauten Garbage Collection robuster macht. Deno besteht zudem aus mehreren Komponenten in Form sogenannter Rust Crates, die Entwickler bei Bedarf wiederum in eigene Rust-Programme integrieren. Die entsprechenden APIs gelten derzeit allerdings noch nicht als stabil, die Laufzeitumgebung künftiger Deno-Versionen soll sich zudem über Plugins erweitern lassen.

Abbildung 1: Das Deno-Binary integriert sämtliche Entwicklerwerkzeuge. Das Kommando »deno info« ermittelt die Abhängigkeiten eines Skripts sowie der verwendeten Module.

Abbildung 1: Das Deno-Binary integriert sämtliche Entwicklerwerkzeuge. Das Kommando »deno info« ermittelt die Abhängigkeiten eines Skripts sowie der verwendeten Module.

Der Quellcode unterliegt der MIT License und lagert auf Github [4]. Dort steht Deno auch als Binary für Windows, Mac OS und Linux bereit. Die Laufzeitumgebung selbst besteht aus einer einzigen Datei (Abbildung 1), eine systemweite Installation ist nicht notwendig. Über den Befehl »deno upgrade« aktualisiert sie sich sogar selbst.

Deno lädt das auszuführende Skript wahlweise direkt von einem Webserver, öffnet es von der Festplatte oder nimmt es über die Standardeingabe entgegen. Dem Javascript- beziehungsweise Typescript-Programm darf der Entwickler zudem Parameter mit auf den Weg geben.

Sperrstunde

Ein besonderes Augenmerk legen die Deno-Entwickler auf die Sicherheit. Ähnlich wie im Browser läuft der komplette Code in einer Sandbox, die den Zugriff auf das Betriebssystem blockiert (Abbildung 2). Deno darf anfangs weder das Dateisystem nutzen noch über das Netzwerk kommunizieren, Umgebungsvariablen auslesen, Subprozesse erzeugen oder hochauflösende Zeitmessungen starten, die sich für verschiedene zeitbasierte Angriffe und zum Fingerprinting missbrauchen lassen.

Wer die genannten Komponenten dennoch verwenden möchte, muss beim Start eines Skripts die jeweilige Berechtigung explizit über einen entsprechenden Parameter einräumen. Der folgende Befehl gestattet beispielsweise dem lokalen Skript »webserver.js« den Zugriff auf das Netzwerk (»–allow-net«):

$ deno run --allow-net webserver.js

Die Blockade lässt sich dabei immer nur komplett aufheben. Im Beispiel darf daher nicht nur das Skript »webserver.js«, sondern auch der von ihm eingebundene beziehungsweise importierte Code über das Netz kommunizieren. Immerhin beschränkt Deno bei Bedarf den Zugriff auf zuvor festgelegte Domains oder IP-Adressen. Darüber hinausgehende oder feiner abgestufte Zugriffskontrollen sieht Deno derzeit nicht vor.

Abbildung 2: Hier verlangt das Skript »myserver.ts« Zugriff auf das Netzwerk, was Deno mit einem entsprechenden Hinweis quittiert, um sich dann zu beenden. Erst mit dem korrekten Parameter läuft das Skript durch.

Abbildung 2: Hier verlangt das Skript »myserver.ts« Zugriff auf das Netzwerk, was Deno mit einem entsprechenden Hinweis quittiert, um sich dann zu beenden. Erst mit dem korrekten Parameter läuft das Skript durch.

Interieur

Deno offeriert Entwicklern von Haus aus einige Javascript-APIs und Funktionen. Bei deren Zusammenstellung folgten die Deno-Entwickler eng den derzeit existierenden Browser-APIs. Programmierer greifen somit auf viele gewohnte Funktionen zurück, wie etwa »fetch()«. Eine Liste aller unterstützten Javascript-APIs hält die ausführliche Online-Dokumentation [5] bereit.

Abhängig von der allgemeinen Webbrowser-Entwicklung können sich die bereitgestellten Funktionen allerdings in kommenden Deno-Versionen ändern. Die Kompatibilität zu aktuellen Browser-APIs ist dem Deno-Team an dieser Stelle wichtiger als Abwärtskompatibilität.

Anders sieht dies bei einigen nicht standardisierten Funktionen aus, die der Namensraum »Deno« kapselt. Sie ermöglichen unter anderem einen einfachen Zugriff auf das Dateisystem, die Kommunikation per TCP oder das Starten von Subprozessen. Die Deno-Entwickler versprechen, an dieser API keine inkompatiblen Änderungen vorzunehmen.

Es können jedoch künftig immer wieder neue Funktionen hinzukommen. Einige befinden sich bereits in der Entwicklung und lassen sich über den Parameter »–unstable« freischalten. Sowohl die Funktionen aus der Browser-API als auch jene aus dem »Deno«-Namespace stellt die Laufzeitumgebung bereit. Sie stecken somit direkt im Deno-Binary.

Paketdienst

Entwickler verteilen ihren Code bei Bedarf auf mehrere Skriptdateien, wobei Deno den Standard ES Modules zugrundelegt. Die Einbindung der Module erfolgt per »import«-Anweisung, »require()« unterstützt Deno anders als Node.js explizit nicht mehr.

Die Laufzeitumgebung angelt alle importierten Module automatisch aus dem Internet, Entwickler hinterlegen einfach die passenden URLs (Abbildung 3). Ein Beispiel zeigt Listing 1: Es bindet das HTTP-Modul ein und startet den darin enthaltenen kleinen Webserver. Die URL zeigt dabei immer direkt auf den Quellcode, im Beispiel auf das Skript »server.ts«.

Listing 1

Einfacher Webserver in Deno

import { serve } from "https://deno.land/std@0.59.0/http/server.ts";
const myserver = serve({ port: 8000 });
for await (const req of myserver) {
  req.respond({ body: "Hallo Welt!\n" });
}

Der in Listing 1 verwendete HTTP-Server gehört zu den sogenannten Standardmodulen. Dabei handelt es sich um eine Sammlung nützlicher Typescript-Module, die einige häufig wiederkehrende Aufgaben erleichtern. Darunter finden sich neben der Implementierung eines HTTP-Servers auch Hash-Algorithmen und Funktionen zum Umgang mit Tar-Archiven.

Die Module befinden sich allerdings noch in der Entwicklung, was auch die niedrige Versionsnummer in der URL andeutet. Die Standardmodule möchte das Deno-Team in Zukunft um weitere ergänzen, Hilfen aus der Community sind willkommen. Den Quellcode unterziehen die Deno-Entwickler dabei stets einer eingehenden Prüfung, was eine hohe Qualität garantieren soll.

Die Standardmodule greifen durchweg auf die Funktionen im Namespace »Deno« zurück und sind folglich nicht mit Node.js kompatibel. Das gilt auch für den HTTP-Server, der über einen nativen TCP-Socket kommuniziert. Nach eigenen Messungen der Entwickler [6] arbeitet Deno mit rund 25 000 Requests pro Sekunde derzeit etwas langsamer als ein vergleichbares Node.js-Skript. Das Deno-Team versucht, die Geschwindigkeit weiter zu optimieren, aktuell sollte sie jedoch bereits für die meisten Anwendungen genügen.

Abbildung 3: Ruft ein Entwickler wie hier eine URL zu einem Standardmodul direkt auf, landet er im Quellcode und springt über den entsprechenden Link zur passenden Stelle in der Dokumentation.

Abbildung 3: Ruft ein Entwickler wie hier eine URL zu einem Standardmodul direkt auf, landet er im Quellcode und springt über den entsprechenden Link zur passenden Stelle in der Dokumentation.

Einzelgänger

Abhängigkeiten zwischen Modulen löst Deno selbst auf. Anders als bei Node.js benötigt man somit weder einen externen Paketmanager wie NPM noch Konfigurationsdateien. Im Gegenzug darf Deno keine NPM-Pakete nutzen, wodurch eine Vielzahl bereits vorhandener Lösungen und Bibliotheken nicht direkt unter Deno läuft. Eine Kompatibilitätsschicht existiere zwar, ist aber laut den Deno-Machern noch weit von der Praxistauglichkeit entfernt.

Es gibt einen weiteren Nachteil: Bindet der Entwickler in einem größeren Projekt ein Modul an mehreren Stellen ein, muss er darauf achten, überall dieselbe Version des Moduls zu importieren. Umgehen lässt sich das mit sogenannten Import Maps, deren Entwicklung sich aber ebenfalls noch im Fluss befindet. Mit ihnen legt der Entwickler in einer externen JSON-Datei die benötigten URLs ab und weist ihnen jeweils ein Alias zu, über das er die Module dann in seinem Projekt importiert.

In jedem Fall lagert Deno den nachgeladenen Code in einem Cache, den es während der Ausführung des Skripts auch nicht mehr eigenmächtig aktualisiert. Damit arbeitet das Skript auch dann weiter, wenn die Netzwerkverbindung abbricht. Auf Wunsch erzeugt Deno eine (riesige) Javascript-Datei mit sämtlichen Abhängigkeiten zur Ausführung.

Während der Entwicklung hilft Deno mit eingebauten Werkzeugen beim Abwickeln und Auswerten von Tests. Ein noch experimenteller Linter klopft den Code auf typische Fehler ab. Passend dazu unterstützt Deno das V8 Inspector Protocol, über das sich Debugger in laufende Skripte einklinken. Dieses Protokoll sprechen unter anderem Entwicklerwerkzeuge von Chrome. Ein integrierter Dependency Inspector dröselt die Abhängigkeiten der Module auf, ein Formatter bringt den Quellcode in eine einheitliche Struktur. Obendrein bastelt Deno auf Wunsch noch aus JSDoc-Anweisungen im Quellcode eine einfache Dokumentation.

Fazit

Deno soll ein eigenständiges Werkzeug sein, mit dem sich schnell komplexe Funktionen skripten lassen. Dieses Ziel scheinen die Entwickler bereits erreicht zu haben, wenngleich noch einige Baustellen warten. Vor allem die Standardmodule verlangen noch etwas Nacharbeit. Aufgrund einer abweichenden API und des fehlenden Supports für NPM-Module erfordern bestehende Node.js-Skripte derzeit meist eine Anpassung an Deno. Schließlich weist die neue Runtime noch keine so große aktive Community auf wie der erklärte Konkurrent Node.js.

Deno bietet allerdings einige äußerst interessante Konzepte, wie etwa die gegenüber Node.js erhöhte Sicherheit und den eingebauten Paketmanager. Sofern ein Skript nicht den Namensraum »Deno« einbindet, läuft es unverändert in Webbrowsern. Aufgrund seiner Vorteile sollten Entwickler einen Blick auf Deno werfen und der Runtime vielleicht bei einem neuen kleinen Projekt eine Chance geben. (kki)

Infos

  1. Node.js: https://nodejs.org/de/

  2. “10 Things I Regret About Node.js” (Video): https://www.youtube.com/watch?v=M3BM9TB-8yA

  3. Deno: https://deno.land

  4. Deno auf Github: https://github.com/denoland

  5. Deno-Handbuch: https://deno.land/manual/

  6. Ankündigung der Deno-Version 1.0: https://deno.land/v1

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben