Zero-Server-Side-Browser-Tools: Entwicklung sicherer Werkzeuge mit Vanilla JavaScript

Management Summary
Ein Browserwerkzeug kann Berechnungen oder Textumwandlungen direkt auf dem Gerät ausführen, auf dem die Seite geöffnet ist. Die eingegebenen Inhalte müssen dafür nicht an einen Anwendungsserver geschickt werden. Das kann kleine Hilfsprogramme datensparsamer machen. Der Beitrag erklärt diesen Ansatz und seine Grenzen: Bereits das Laden der Seite erzeugt eine Verbindung zum Betreiber, und weiterer Code auf derselben Seite kann Zugriff auf Eingaben haben. Lokale Verarbeitung allein schützt deshalb nicht umfassend.
Inhalt
Ein lokales Browserwerkzeug kann Eingaben hashen, auswerten oder umformen, ohne diese Inhalte an einen Anwendungsserver zu übertragen. Der Vorteil ist ein kleinerer Datenfluss: Ein Verarbeitungsendpunkt benötigt die sensiblen Inhalte nicht. Die Seite selbst kann weiterhin aus dem Netz geladen werden; dabei fallen beim Hostingserver Anfragemetadaten an, etwa IP-Adresse, aufgerufener Pfad und Zeitpunkt. Die Aussage „lokal verarbeitet“ braucht deshalb einen klaren Geltungsbereich.
Lokale Verarbeitung hat eine Vertrauensgrenze
Die Browser-Sandbox trennt Webinhalte von Teilen des Betriebssystems. Code auf derselben Seite kann trotzdem Eingabefelder lesen. Ein kompromittiertes Skript, unsicher eingefügtes HTML oder eine Browsererweiterung mit passenden Rechten kann die Vertraulichkeit beeinträchtigen. Ein Framework ist ein Softwaregrundgerüst mit vorgegebenen Strukturen und wiederverwendbaren Funktionen. Vanilla JavaScript bezeichnet Code ohne Framework und ist keine Sicherheitsgarantie.
Wenige Abhängigkeiten, HTTPS-Auslieferung, geprüfter Quellcode und der Verzicht auf Analytics oder Sitzungsaufzeichnungen im Eingabebereich verringern Risiken. Eingaben gehören nicht in URLs, Anwendungsprotokolle oder Fehlertelemetrie. Letztere übermittelt Diagnoseinformationen über aufgetretene Fehler. Beliebiger Text wird über textContent oder das value eines Eingabefeldes ausgegeben, nicht über innerHTML.

Die vorgesehenen Bytes hashen
UTF-8 legt fest, wie Zeichen in Bytes umgewandelt werden. Bytes sind die Zahleneinheiten, die ein Computer speichert oder überträgt. TextEncoder kodiert JavaScript-Text als UTF-8-Bytes. TextDecoder wandelt Bytes einer ausgewählten Kodierung zurück in Text. Auch diese Vorgänge benötigen Speicher und Rechenzeit. Ein Promise steht für ein Ergebnis, das erst später bereitsteht. crypto.subtle.digest() liefert ein Promise; vorbereitende Textumwandlungen und Schleifen können den Hauptthread trotzdem beschäftigen. Hardwarebeschleunigung und fehlende Latenz sind nicht garantiert.
SHA-256 berechnet einen Fingerabdruck fester Länge aus den Eingabebytes. Das Beispiel berechnet SHA-256 über die UTF-8-Darstellung, ohne Leerzeichen zu entfernen oder Großbuchstaben zu verändern. Die Grenze von einer Million UTF-16-Codeeinheiten ist eine beispielhafte Anwendungsgrenze, keine Browsergarantie und keine Byte-Grenze. Manche Zeichen, etwa viele Emojis, benötigen zwei UTF-16-Codeeinheiten. Sichtbare Zeichen werden damit nicht direkt gezählt. Eine aufrufende Oberfläche muss abgelehnte Eingaben und Fehler verständlich anzeigen.
async function sha256Text(text) {
if (typeof text !== 'string') throw new TypeError('Expected text');
if (text.length > 1_000_000) throw new RangeError('Input too large');
if (!globalThis.isSecureContext || !globalThis.crypto?.subtle) {
throw new Error('Web Crypto requires a supported secure context');
}
const bytes = new TextEncoder().encode(text);
const digest = await crypto.subtle.digest('SHA-256', bytes);
return Array.from(new Uint8Array(digest),
byte => byte.toString(16).padStart(2, '0')).join('');
}
Für eine Dateiprüfsumme sind die ursprünglichen Datei-Bytes maßgeblich, nicht erneut dekodierter und kodierter Text. digest() benötigt die vollständige Eingabe im Speicher und unterstützt kein schrittweises Streaming. Sehr große Dateien erfordern eine andere geprüfte Implementierung und gemessene Ressourcengrenzen. SHA-256 allein ist weder Verschlüsselung noch ein geeignetes Verfahren zur Passwortspeicherung. Vorhersagbare personenbezogene Angaben werden durch einen Hash nicht automatisch anonym.
CSP begrenzt bestimmte Übertragungswege
connect-src 'self' erlaubt Skriptverbindungen zum eigenen Origin, der Kombination aus Protokoll, Hostname und Port. connect-src 'none' blockiert die von dieser Direktive erfassten Verbindungs-APIs, etwa fetch, WebSocket und Beacons. Für Bilder, Formulare, Frames und Skriptdateien gelten eigene Regeln. Die Richtlinie eines lokalen Werkzeugs muss auch diese Wege berücksichtigen und ausführbaren Code eng begrenzen.
Eine Content Security Policy (CSP) regelt erlaubte Ressourcen und Aktionen auf einer Seite. CSP wird im Browser durchgesetzt und bietet eine zusätzliche Schutzschicht, ist aber keine allgemeine Netzwerk-Firewall und kein mathematischer Ausschluss von Datenabfluss. Navigation und erlaubte Ressourcenabrufe benötigen eine eigene Betrachtung. Auch selbst gehostete Skripte können kompromittiert sein. Eine durchgesetzte Richtlinie muss geprüft werden; Report-Only meldet Verstöße, blockiert aber nicht.
Workers verbessern die Reaktionsfähigkeit, nicht die Geheimhaltung
Ein Web Worker führt JavaScript in einem getrennten Ausführungsbereich aus. Dadurch kann aufwendige Berechnung vom UI-Thread getrennt werden, der die Bedienung der Seite verarbeitet. Das DOM ist die Baumstruktur der Seitenelemente. Direkter DOM-Zugriff fehlt im Worker, doch die Seite übergibt Eingabe und empfängt Ergebnis. Schädlicher Seitencode kann deshalb an beiden Übergabepunkten mitlesen. Auch Workers können Netzwerkanfragen ausführen.
Eine getrennte Workerdatei benötigt den passenden CSP-Antwortheader für den eigenen Ausführungskontext. worker-src der Seite steuert das Laden des Workers, nicht automatisch dessen spätere Verbindungen. Blob-Workers erben die Richtlinie des Erzeugers und benötigen eine passende Ladefreigabe. Eine separate Datei erleichtert die Prüfung; eine Prüfsumme allein belegt jedoch kein netzwerkfreies Verhalten.
Nachweise und verbleibende Grenzen
Eine sinnvolle Prüfung umfasst Laden, Eingabe, Verarbeitung, Kopieren oder Download, Fehlerfälle und Leeren. Zur Netzwerkprüfung gehören Workers und gegebenenfalls aktive Service Workers. Ein Service Worker kann Anfragen einer Website abfangen und gespeicherte Antworten liefern. Ein Offline-Durchlauf nach dem Laden belegt den geprüften Stand und Ablauf, nicht jede spätere Version. Testumgebungen mit gesperrtem Netzwerk liefern einen zusätzlichen Nachweis.
Leere Felder und verworfene Referenzen verringern versehentliche Wiederverwendung. JavaScript kann aber keine forensische Löschung sämtlicher Kopien im Browser- oder Betriebssystemspeicher zusichern. Zwischenablage, Downloads und absichtlich gespeicherte Daten können einen Tab überdauern. Unnötige Speicherung zu vermeiden ist belastbarer als vollständige Löschung beim Schließen zu versprechen.
Fragen und Antworten
Gilt der sichere Kontext auch bei lokaler Entwicklung?
crypto.subtle benötigt einen unterstützten sicheren Kontext. HTTPS und normale Entwicklung über localhost sind geeignet. Eine einfache HTTP-Adresse im privaten LAN gilt normalerweise nicht als sicherer Kontext. Das Beispiel prüft sowohl den Kontext als auch die Verfügbarkeit der API und meldet fehlende Unterstützung.
Beweist ein leerer Netzwerk-Reiter die Vertraulichkeit?
Nur der beobachtete Ablauf ist damit dokumentiert. Ein ergänzender Offline-Test, eine geprüfte durchgesetzte CSP und Kontrolle der ausgelieferten Abhängigkeiten stärken den Nachweis. Ein einzelner Test ersetzt keine Prüfung späterer Änderungen.
Quellen
- MDN Web Docs
- OWASP Top 10
- Web Crypto digest: sicherer Kontext, Promise, kein Streaming
- TextEncoder: UTF-8-Kodierung
- TextDecoder: Bytes in Text umwandeln
- Workers: Kommunikation und CSP
- CSP: Geltungsbereich von connect-src
- Sichere Kontexte und localhost
- Content Security Policy Level 3
- OWASP: sichere Textausgabe im DOM
- JavaScript: Speicherverwaltung
- OWASP: Passwort-Hashing