Das Deno-Berechtigungsmodell erklärt
Deno-Berechtigungen erklärt: allow- und deny-Flags, Scoping, deno.json-Permission-Sets, Permissions API und Sicherheitsfallen.
Deno führt Ihren Code in einer Sandbox aus, die zunächst gar nichts gewährt: Dateisystem, Netzwerk, Umgebungsvariablen, Subprozesse, Systeminformationen und native Bibliotheken (FFI) sind sämtlich gesperrt, bis Sie sie einzeln über ein --allow-*-Flag freischalten. Und nahezu jedes Flag akzeptiert ein Argument, das die Freigabe auf bestimmte Pfade, Hosts oder Variablen eingrenzt.
Wenn Sie von Node kommen, wird Ihr erstes Deno-Skript mit hoher Wahrscheinlichkeit an einem Berechtigungsfehler scheitern – und die Lösung ist selten das pauschale -A, nach dem das Muskelgedächtnis greift. Herauszufinden, welches Flag hinzugefügt werden muss und wie eng es einzugrenzen ist, macht den größten Teil der Lernkurve aus.
Das ist die Umkehrung des historischen Node.js-Standardverhaltens und die wichtigste Sache, die Sie verstehen müssen, bevor Sie ein Deno-Skript ausführen. Dieser Artikel behandelt, was standardmäßig blockiert ist, jedes --allow-*- und --deny-*-Flag samt Scoping-Syntax, die Neuerungen in Deno 2.x (Vorrang von Deny-Flags, --allow-sys, --allow-import, Wildcards für Umgebungsvariablen), Berechtigungssätze in deno.json, die Permissions API zur Laufzeit sowie die zwei Sicherheitsfallen, über die die Dokumentation am wenigsten sagt.
Die wichtigsten Erkenntnisse
- Standardmäßig kann Deno-Code keine Dateien lesen oder schreiben, keine Netzwerkverbindungen öffnen, keine Umgebungsvariablen auslesen, keine Subprozesse starten, nicht auf Systeminformationen zugreifen und keine nativen Bibliotheken laden. Sie aktivieren jede Fähigkeit einzeln über ein
--allow-*-Flag. - Zu jedem
--allow-*-Flag existiert ein--deny-*-Gegenstück, und Deny gewinnt immer:--allow-read=. --deny-read=./secretsgibt das Projektverzeichnis frei, hält./secretsaber unlesbar. - In Deno 2 löst eine verweigerte Fähigkeit einen
Deno.errors.NotCapableaus (umbenannt vom früherenPermissionDenied), wodurch sich Denos Berechtigungsverweigerungen von gewöhnlichen Betriebssystemfehlern unterscheiden lassen. - Seit Deno 2.5 können Sie benannte Berechtigungssätze in
deno.jsondefinieren und sie mit-P=nameanwenden (oder einendefault-Satz mit einem bloßen-P), sodass Least-Privilege-Flags unter Versionskontrolle bleiben. - Nichts im initialen statischen Importgraphen wird vor dem Laden gegen das Berechtigungssystem geprüft, und
--allow-runführt Subprozesse außerhalb der Sandbox aus. Das sind die zwei Wege, auf denen nicht vertrauenswürdiger Code entkommt.
Warum ist Deno standardmäßig sicher?
Nichts läuft mit implizit vorhandenen Privilegien: Festplatte, Netzwerk, Umgebung und das Starten von Subprozessen bleiben verschlossen, bis Sie sie öffnen. Diese Designentscheidung kam direkt von Ryan Dahl, dem ursprünglichen Node-Schöpfer, der Deno entwickelte, um Nodes „Vollzugriff auf alles”-Standard umzukehren. In Deno erhalten Abhängigkeiten keine eigene implizite Autorität; in Node erbt ein Paket sämtliche System-I/O, die der umgebende Prozess erreichen kann – und genau diese Lücke ist der markanteste Unterschied zwischen den beiden Runtimes.
Node hat inzwischen ein eigenes Berechtigungsmodell nachgerüstet. Es erschien experimentell in Node 20 hinter --experimental-permission, wurde in v23.5.0 als stabil markiert, und Node 24 hat die experimentelle Schreibweise zugunsten des schlichten --permission abgelöst. Denos Modell greift jedoch weiter: Es ist der Standard und nicht ein Opt-in-Flag, und es deckt mehr Fähigkeitsklassen mit feinerem Scoping ab.
Discover how at OpenReplay.com.
Was sind Denos --allow-*-Berechtigungsflags?
Jede Fähigkeit ist einem Flag zugeordnet, und die meisten Flags akzeptieren eine Allow-List als Argument. Ein Flag ohne Argument gewährt alles in dieser Klasse; ein Argument grenzt es ein. Ein bloßes --allow-net gewährt Zugriff auf jeden Host auf jedem Port, während --allow-net=api.example.com:443 das Programm auf genau einen Host und Port beschränkt.
| Flag | Schützt | Eingegrenztes Beispiel | Deny-Gegenstück |
|---|---|---|---|
--allow-read | Lesezugriffe auf das Dateisystem | --allow-read=./data,config.ini | --deny-read |
--allow-write | Schreibzugriffe auf das Dateisystem | --allow-write=./tmp | --deny-write |
--allow-net | Netzwerkzugriff | --allow-net=api.example.com:443 | --deny-net |
--allow-env | Umgebungsvariablen | --allow-env=PORT,HOST | --deny-env |
--allow-run | Subprozesse | --allow-run=git,deno | --deny-run |
--allow-sys | Systeminfo-APIs | --allow-sys=hostname | --deny-sys |
--allow-ffi | Native Bibliotheken | --allow-ffi=./lib.so | --deny-ffi |
--allow-import | Remote-HTTPS-Imports | --allow-import=jsr.io | --deny-import |
Beachten Sie, dass es kein --allow-hrtime gibt. Dieses Flag wurde in Deno 2.0 entfernt, und hochauflösende Timing-APIs wie performance.now() sind nun immer verfügbar.
Wenn ein Skript eine Berechtigung benötigt, die Sie nicht gewährt haben, hält Deno an und fragt interaktiv nach:
┏ ⚠️ Deno requests net access to "deno.com:443".
┠─ Requested by `fetch()` API.
┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) >
Antworten Sie mit y, um einmalig zu gewähren, mit n, um zu verweigern (was einen Deno.errors.NotCapable auslöst), oder mit A, um die gesamte Klasse zuzulassen. In CI-Umgebungen sollten Sie die Flags von Anfang an übergeben, damit nichts an einer Eingabeaufforderung hängen bleibt.
Wie der Flag-Satz gewachsen ist: Vorrang von Deny, --allow-sys, --allow-import, Wildcards für env
Deny-Flags kamen mit Deno 1.36 (August 2023), und seither hat jedes --allow-*-Flag ein --deny-*-Gegenstück. Wo sich beide überschneiden, greift die Verweigerung – so können Sie breit gewähren und Ausnahmen ausschneiden:
deno run --allow-read=. --deny-read=./secrets app.ts
--allow-sys, das bis Deno 1.26 (Oktober 2022) zurückreicht, kontrolliert Systeminfo-APIs wie Deno.hostname() und Deno.systemMemoryInfo(). Die einzige wirklich neue Fähigkeitsklasse in Deno 2.0 war --allow-import, das regelt, von welchen HTTPS-Hosts Ihr Code zur Laufzeit Module beziehen darf; einfaches HTTP ist niemals erlaubt, statische Imports werden automatisch gegen die Liste gefiltert, und das Benennen eigener Hosts ersetzt Denos eingebauten Satz, anstatt ihn zu ergänzen. Verwenden Sie --deny-import, um bestimmte Hosts vollständig zu blockieren.
Der Zugriff auf Umgebungsvariablen erhielt in Deno 2.1 Suffix-Wildcards. Anstatt jede Variable aufzulisten, grenzen Sie per Präfix ein:
deno run --allow-env="AWS_*" main.ts
Berechtigungen in deno.json deklarieren
Seit Deno 2.5 können Sie benannte Berechtigungssätze in deno.json definieren und mit -P=name (oder --permission-set=name) anwenden, sodass Least-Privilege-Flags unter Versionskontrolle bleiben, statt sie bei jedem Lauf neu eintippen zu müssen. Die Objektschlüssel entsprechen den Flag-Namen (read, write, net, env, sys, run, ffi, import), wie in der deno.json-Referenz dokumentiert:
{
"permissions": {
"default": {
"read": ["./deno.json"],
"env": true,
"run": { "allow": ["git"] }
},
"process-data": {
"read": ["./data"],
"write": ["./data"]
}
},
"tasks": {
"dev": "deno run -P main.ts"
}
}
Führen Sie deno run -P=process-data main.ts für den benannten Satz aus oder deno run -P main.ts für den default-Satz. Deno 2.5 hat außerdem die Umgebungsvariable DENO_AUDIT_PERMISSIONS eingeführt: Zeigen Sie damit auf einen Dateipfad, und Deno hängt für jede Berechtigung, die das Programm berührt, einen JSONL-Eintrag an – unabhängig davon, ob der Zugriff gewährt oder verweigert wurde. Das ist ein schneller Weg, um herauszufinden, was ein Skript tatsächlich benötigt.
Die Permissions API zur Laufzeit
Fragen Sie Berechtigungen im Code vor einer eingeschränkten Operation ab, um kontrolliert zu scheitern, statt an einem NotCapable-Fehler abzustürzen. Deno.permissions stellt query, request und revoke bereit, die jeweils einen Descriptor wie { name: "net", host: "example.com" } entgegennehmen:
const desc = { name: "net", host: "example.com" } as const;
let status = await Deno.permissions.query(desc); // "prompt" | "granted" | "denied"
if (status.state === "prompt") {
status = await Deno.permissions.request(desc); // triggers the y/n/A prompt
}
if (status.state === "granted") {
await fetch("https://example.com");
}
await Deno.permissions.revoke(desc); // drop it again
query meldet den aktuellen Zustand ohne Nachfrage, request fragt den Nutzer, wenn der Zustand noch prompt ist, und revoke gibt eine Fähigkeit wieder zurück. Damit können langlaufende Programme prüfen, bevor sie eine Ressource anfassen, und einen anderen Weg einschlagen, wenn der Zugriff nicht verfügbar ist.
Die zwei Fallstricke: Imports und --allow-run
Zwei Verhaltensweisen erlauben es nicht vertrauenswürdigem Code, die Sandbox zu umgehen – beide sollten Sie sich einprägen. Erstens: Alles, was Deno von Ihrem Entry Point aus statisch auflösen kann (lokale Dateien, npm- und JSR-Pakete sowie als String-Literale ausgeschriebene Remote-URLs), wird geladen, bevor das Berechtigungssystem überhaupt mitreden darf. Eine Abhängigkeit kann also ihren eigenen Quellcode lesen und das Netzwerk erreichen, noch bevor Ihr erstes --allow-*-Flag greift. Dieser Freifahrtschein gilt ausschließlich für das Laden: Sobald der Code ausgeführt wird, wird jede Operation wieder geprüft. --allow-import grenzt ein, welche Remote-Hosts importiert werden dürfen, macht Imports selbst aber nicht von einer Laufzeitfreigabe abhängig – prüfen Sie Code Dritter also, bevor Sie ihn einbinden.
Zweitens ist --allow-run der schärfste Fallstrick: Was Sie starten, wird zu einem eigenständigen Prozess und trägt die Privilegien, die das Betriebssystem ihm gewährt, nicht den engen Satz, den Sie Deno übergeben haben. Das bedeutet: --allow-run=deno erlaubt es einem gesandboxten Skript, Deno mit --allow-all neu zu starten und vollständig zu entkommen. Zudem schränkt es nur ein, welche ausführbare Datei läuft, nicht deren Argumente: --allow-run=cat erlaubt Code, über cat jede beliebige Datei zu lesen. Grenzen Sie es auf bestimmte, vertrauenswürdige Binaries wie --allow-run=git ein – und beachten Sie, dass --allow-ffi dieselbe Risikoklasse mitbringt, da native Bibliotheken als Maschinencode außerhalb der Prüfungen auf JavaScript-Ebene ausgeführt werden.
Die pragmatische Haltung: Gewähren Sie die engstmögliche Allow-List, die funktioniert, ergänzen Sie --deny-* für sensible Pfade, und behandeln Sie --allow-run und --allow-ffi als Vertrauensgrenzen, nicht als Bequemlichkeiten. Beginnen Sie mit null Berechtigungen, führen Sie das Skript aus und fügen Sie genau das hinzu, was die Eingabeaufforderungen (oder das DENO_AUDIT_PERMISSIONS-Log) als notwendig ausweisen.
FAQs
Was ist der Unterschied zwischen einer verneinten Deno-Berechtigungsanfrage und Deno.errors.NotCapable?
Es ist dasselbe Ergebnis über verschiedene Wege. Wenn Sie eine interaktive Anfrage mit 'n' beantworten oder ohne das erforderliche Flag ausführen, löst die verweigerte Operation in Deno 2 einen Deno.errors.NotCapable aus (umbenannt vom früheren PermissionDenied). Die Umbenennung erlaubt es, Denos eigene Berechtigungsverweigerungen von gewöhnlichen Betriebssystemfehlern wie einer fehlenden Datei zu unterscheiden, da beide zuvor als ähnlich aussehende Fehler auftraten.
Erlaubt --allow-net=example.com auch HTTPS auf Port 443?
Ja. Wenn Sie einen Host ohne Port angeben, etwa --allow-net=example.com, erlaubt Deno Verbindungen zu diesem Host auf jedem Port, einschließlich 443. Um auf einen einzelnen Port zu beschränken, müssen Sie ihn explizit als --allow-net=example.com:443 angeben, was dann alle anderen Ports dieses Hosts blockiert. Ein bloßes --allow-net ohne Argument gewährt jeden Host auf jedem Port.
Kann ich --allow-read mit --deny-read für sich überschneidende Pfade kombinieren?
Ja, und Deny gewinnt immer. Der Aufruf --allow-read=. --deny-read=./secrets gewährt Lesezugriff auf das gesamte Projektverzeichnis mit Ausnahme von ./secrets, das unlesbar bleibt. Deny-Flags haben in jeder Fähigkeitsklasse Vorrang vor Allow-Flags, sodass Sie mit diesem Muster breit gewähren und sensible Pfade ausschneiden können, statt jede erlaubte Datei einzeln aufzuzählen.
Benötige ich --allow-import, um npm- oder JSR-Pakete zu verwenden?
Nein, nicht für statisch importierte Pakete. Alles, was Deno von Ihrem Entry Point aus ohne Codeausführung auflösen kann – npm- und JSR-Pakete eingeschlossen – wird geladen, bevor das Berechtigungssystem konsultiert wird. --allow-import entscheidet, von welchen HTTPS-Hosts Remote-Imports kommen dürfen; einfaches HTTP ist nie eine Option. Ein zur Laufzeit berechneter Specifier ist ein anderer Fall: Eine dynamische Remote-URL benötigt --allow-import, ein dynamischer lokaler Pfad benötigt --allow-read.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k