Ist Vaultwarden sicher? Risiken und Hardening
Vaultwarden verschlüsselt Tresorinhalte im Client, speichert Kontometadaten aber im Klartext. Sichern Sie vor allem Admin-Token, Ports und Backup-Dateien.
Ist Vaultwarden sicher? Die kurze Antwort
Vaultwarden ist an der wichtigsten Stelle sicher: Jedes Tresorelement wird auf Ihrem Gerät verschlüsselt, bevor es den Server erreicht. Der Server speichert die verschlüsselten Tresorinhalte zusammen mit Kontometadaten im Klartext. Wer die gesamte Datenbank kopiert, benötigt weiterhin die Verschlüsselungsschlüssel des Tresors, um die Inhalte zu lesen. E-Mail-Adressen der Konten und Hinweise zu Passwörtern sind jedoch sofort sichtbar.
Diese Antwort beruht auf mehreren Voraussetzungen. Fehler entstehen an den Stellen, die Sie konfigurieren. Ein Administrationsbereich hinter einem erratbaren Token. Ein Container-Port, der für das gesamte Internet veröffentlicht ist. Ein Klartext-config.json. Ein Backup-Tarball in einem Home-Verzeichnis auf demselben Server. Keines dieser Probleme betrifft die Kryptografie. Sie alle sind Gründe dafür, dass selbst gehostete Tresore geleert werden.
Im Folgenden wird eine funktionierende Installation vorausgesetzt. Wenn Sie noch keine haben, richten Sie Vaultwarden zuerst mit der Vaultwarden-Installationsanleitung für einen VPS ein. Arbeiten Sie diese Liste anschließend in der angegebenen Reihenfolge durch.
Was der Server tatsächlich speichert
Vaultwarden verwendet das Datenmodell von Bitwarden. Name, Benutzername, Passwort, Notizen und URIs eines Tresoreintrags werden auf dem Client mit einem zufällig erzeugten Eintragsschlüssel verschlüsselt, bevor eine Anfrage gesendet wird. Dieser Schlüssel wird durch den symmetrischen Schlüssel des Benutzers oder der Organisation geschützt. Der aus dem Passwort abgeleitete Schlüssel schützt den symmetrischen Schlüssel des Benutzers. Der Inhalt von Anhangsdateien verwendet einen separaten Anhangsschlüssel, der durch den Eintragsschlüssel geschützt wird. Der Server erhält undurchsichtige Daten mit einer daran angehängten UUID (universally unique identifier).
Einige Daten sind nicht verschlüsselt. Sie sollten genau wissen, um welche Daten es sich handelt:
- Ihre Konto-E-Mail-Adresse und der Hinweis auf das Master-Passwort im Klartext.
- Ihre KDF-Einstellungen (key derivation function) und der Salt, weil der Client diese Daten benötigt, um den Schlüssel bei der nächsten Anmeldung erneut zu erzeugen.
- Ein serverseitiger Hash des Master-Passwort-Hashs, den der Client sendet. Er dient dazu, die Anmeldung selbst zu authentifizieren.
- Metadaten: Organisationszugehörigkeit, Gerätenamen und Zeitpunkte der letzten Anmeldung.
- Das TOTP-Secret für die authenticatorbasierte Zwei-Faktor-Anmeldung. Es liegt in der
twofactor-Tabelle unverschlüsselt vor, weil der Server den erwarteten Code berechnen und mit Ihrem Code vergleichen muss. Das ist nicht dasselbe wie ein TOTP-Secret (time-based one-time password), das Sie in einem Tresoreintrag speichern. Dieses Secret wird wie jedes andere Feld verschlüsselt.
Der Datenordner ist klein. Bei einer Docker-Installation ist es der Ordner, den Sie auf /data gemountet haben.
sudo ls -l /vw-data/db.sqlite3 enthält fast den gesamten Zustand. attachments/ enthält hochgeladene Dateien in UUID-Verzeichnissen außerhalb der Datenbanktabellen. sends/ enthält Send-Anhänge und ist für die temporäre Speicherung vorgesehen. icon_cache/ kann gelöscht werden. rsa_key.pem und die zugehörigen Dateien signieren die JWTs (JSON web tokens) angemeldeter Benutzer. Eine Kopie dieses privaten Schlüssels kann daher verwendet werden, um eine Vault-Anmeldesitzung zu fälschen. config.json speichert Einstellungen, die über die Administrationsseite gespeichert wurden, einschließlich SMTP-Anmeldedaten im Klartext und des konfigurierten Werts des Admin-Tokens. Wenn Sie die folgenden Argon2-Anweisungen befolgen, wird statt des Klartextpassworts ein Hash des Admin-Tokens gespeichert.
Das praktische Bedrohungsmodell betrifft daher den Dateisystemzugriff und nicht die Netzwerkverschlüsselung. Leseberechtigungen für dieses eine Verzeichnis geben Zugriff auf die E-Mail-Adressen aller Benutzer, ihre 2FA-Secrets für die Anmeldung, einen Schlüssel zum Fälschen von Sitzungen und eine Offline-Kopie jedes Tresors, die in Ruhe angegriffen werden kann. Jeder folgende Schritt dient dazu, den Zugriff auf dieses Verzeichnis zu verhindern.
Zuerst das Admin-Token absichern
/admin ist eine vollständige Verwaltungsoberfläche: Benutzerliste, Einladungen, Löschvorgänge und sämtliche Laufzeiteinstellungen. Sie ist ausschließlich durch ein gemeinsames Secret geschützt. Es gibt keinen Benutzernamen und keine benutzerbezogene Zwei-Faktor-Authentifizierung.
In älteren Anleitungen wird empfohlen, ADMIN_TOKEN mit openssl rand -base64 48 zu erzeugen. Das funktioniert, hinterlässt das Secret aber überall dort im Klartext, wo Sie es konfigurieren. Vaultwarden akzeptiert auch eine Argon2-PHC-Zeichenfolge (Password Hashing Competition), sodass stattdessen ein Hash gespeichert wird. Erzeugen Sie ihn in einem laufenden Container:
docker exec -it vaultwarden /vaultwarden hashOder ohne den laufenden Container überhaupt zu verwenden:
docker run --rm -it vaultwarden/server /vaultwarden hashDer Befehl fragt zweimal nach einem Passwort und gibt anschließend eine Zeile aus, die mit $argon2id$ beginnt. Bei einer Installation direkt auf dem Host führen Sie ./vaultwarden hash aus. Wenn Sie stattdessen die argon2-CLI direkt verwenden möchten, dokumentiert das Projekt die OWASP-Mindestparameter:
echo -n 'MySecretPassword' | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1Nun zur Falle, die häufig eine Stunde kostet. Eine PHC-Zeichenfolge enthält viele Zeichen $, und Docker Compose behandelt $ als Variableninterpolation. Wenn Sie sie ohne Escaping in einen environment:-Block einfügen, wird der Wert, der den Container erreicht, verändert. Daher weist /admin ein Token zurück, von dessen Richtigkeit Sie überzeugt sind. Es gibt zwei sichere Varianten. Verdoppeln Sie in docker-compose.yml jedes $:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$UUZxK1FZMkZoRHFQRlVrTXZvS0E3bHpNQW55c2dBN2NORzdsa0Nxd1JhND0$$cUoId+JBUsJutlG4rfDZayExfjq4TCt48aBc9qsc3UIIn einer .env-Datei ist kein Escaping erforderlich. Verwenden Sie dort jedoch einfache Anführungszeichen:
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78'Begrenzen Sie anschließend die Rate für die Verwaltungsoberfläche und verkürzen Sie ihre Sitzung:
ADMIN_RATELIMIT_SECONDS=300
ADMIN_RATELIMIT_MAX_BURST=3
ADMIN_SESSION_LIFETIME=20Damit sind zunächst drei Anmeldeanforderungen für die Verwaltung pro Client-IP-Adresse möglich. Anschließend wird das Kontingent mit einer Anforderung alle fünf Minuten wieder aufgefüllt. Richtige und falsche Passwörter werden gleichermaßen gezählt, weil die Begrenzung vor der Passwortprüfung ausgeführt wird. Anforderungen über dem Limit erhalten HTTP 429 mit Too many requests, try again later.. Die Admin-Sitzung läuft 20 Minuten nach der Anmeldung ab, unabhängig davon, ob Sie die Verwaltungsoberfläche noch verwenden. Aktivitäten verlängern die Sitzung nicht.
Noch besser ist es, die Seite zu deaktivieren. Die meisten Installationen benötigen sie einmal, um SMTP zu konfigurieren und die ersten Benutzer einzuladen. Danach wird sie nicht mehr benötigt. Setzen Sie zum Deaktivieren weder ADMIN_TOKEN noch DISABLE_ADMIN_TOKEN, entfernen Sie jeden "admin_token"-Schlüssel aus config.json und erstellen Sie den Container anschließend neu. Das Löschen des Schlüssels aus der Datei ist wichtig, weil die Admin-Seite Einstellungen dorthin schreibt und der Inhalt von config.json Vorrang vor der Umgebung hat. Wenn Sie nur die Variable entfernen, bleibt die Seite erreichbar.
Registrierung schließen, bevor jemand die Domain findet
SIGNUPS_ALLOWED=false
SIGNUPS_VERIFY=true
SHOW_PASSWORD_HINT=falseSIGNUPS_ALLOWED ist standardmäßig auf true gesetzt. Lassen Sie diese Einstellung unverändert, kann jeder, der Ihre Domain erreicht, ein Konto anlegen. Die Daten dieses Kontos liegen dann im selben db.sqlite3 wie Ihre Daten. Setzen Sie die Option auf false und fügen Sie Benutzer über Einladungen aus der Administrationsseite hinzu. SMTP versendet die Einladungs-E-Mail. Ohne SMTP protokolliert der Server eine Einladung für diese Adresse. INVITATIONS_ALLOWED ist standardmäßig ebenfalls true und ermöglicht Organisationsadministratoren, weitere Benutzer einzuladen. Das ist unproblematisch, wenn Sie Ihren Benutzern vertrauen. Auf einer Instanz für einen einzelnen Benutzer sollte die Option false sein. Wenn nur bestimmte Domains eine Registrierung erlauben sollen, ist SIGNUPS_DOMAINS_WHITELIST=example.com restriktiver als eine offene Registrierung, aber deutlich weniger sicher als Einladungen.
SHOW_PASSWORD_HINT ist standardmäßig false und sollte so bleiben. Ist die Option aktiviert und kein SMTP konfiguriert, gibt das Anmeldeformular nach Eingabe einer gültigen E-Mail-Adresse den Hinweis auf das Master-Passwort des entsprechenden Kontos zurück. Dadurch wird der Hinweis offengelegt und zugleich bestätigt, dass die Adresse existiert.
Wenn Ihre Instanz längere Zeit offene Registrierungen erlaubt hat, öffnen Sie die Administrationsseite und prüfen Sie die Benutzerliste, bevor Sie davon ausgehen, dass sich nur Ihr eigenes Konto darauf befindet.
Der Port, den Sie nicht veröffentlichen wollten
Das Docker-Image lauscht innerhalb des Containers auf Port 80. Eine Bare-Metal-Installation verwendet standardmäßig ROCKET_PORT=8000. Der dokumentierte Startbefehl veröffentlicht ihn so:
--publish 127.0.0.1:8000:80Der Präfix 127.0.0.1: ist der entscheidende Teil. Wenn Sie stattdessen -p 8000:80 schreiben, bindet Docker 0.0.0.0. Dazu schreibt Docker DNAT-Regeln (Destination Network Address Translation) in die Tabelle nat. Diese Regeln werden vor den filter-Chains ausgewertet, die ufw verwaltet. Deshalb meldet ufw status den Port als verweigert, während der Port aus dem Internet erreichbar bleibt. Der vollständige Mechanismus wird im Leitfaden zu Docker-Ports, die ufw umgehen erläutert.
Prüfen Sie, worauf tatsächlich gelauscht wird:
sudo ss -tlnp | grep 8000Ein korrektes Ergebnis besteht aus einer einzigen Zeile, die an 127.0.0.1:8000 gebunden ist. Eine an 0.0.0.0:8000 gebundene Zeile bedeutet, dass der Vault direkt erreichbar ist. Korrigieren Sie das Mapping und erstellen Sie den Container anschließend neu. Eine Portbindung wird beim Erstellen des Containers festgelegt, und docker compose restart ändert sie nicht:
docker compose up -d --force-recreateIn älteren Anleitungen findet sich noch ein weiterer Port: 3012, der separate WebSocket-Port. Die Unterstützung dafür wurde in Vaultwarden 1.31.0 entfernt, weil der Benachrichtigungsverkehr auf den primären HTTP-Port verlagert wurde. WEBSOCKET_ENABLED und WEBSOCKET_PORT sind seit 1.29.0 veraltet und werden ignoriert. Der aktuelle Schalter ist ENABLE_WEBSOCKET, dessen Standardwert true lautet. Wenn Ihre Firewall oder Ihre Compose-Datei Port 3012 weiterhin öffnet, schließen Sie ihn.
TLS an einem Reverse Proxy terminieren, nicht in Rocket
Vaultwarden kann TLS (Transport Layer Security) selbst über Rocket bereitstellen, sein Webframework. Das Projekt rät im Produktivbetrieb davon ab. Die integrierte TLS-Unterstützung von Rocket bietet keine strikte Unterstützung für SNI (Server Name Indication). Deshalb empfiehlt die Hardening-Dokumentation außerdem, Ihre Instanz über einen Hostnamen und niemals über eine bloße IP-Adresse aufzurufen. Öffentliche IP-Adressbereiche werden ständig gescannt. Ein Tresor, der auf eine IP-Adresse antwortet, wird dadurch gefunden.
Die relevanten Teile eines nginx-Server-Blocks:
client_max_body_size 525M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}nginx setzt client_max_body_size standardmäßig auf 1m. Ohne Überschreibung schlagen daher nur Request-Bodies fehl, die größer als dieses Limit sind. nginx gibt HTTP 413 an den Client zurück und protokolliert client intended to send too large body: ... bytes. Der abgewiesene Upload erreicht Vaultwarden nicht. Die Header Upgrade und Connection übertragen den WebSocket-Handshake an /notifications/hub. Wenn Sie sie weglassen, funktioniert der Tresor weiterhin. WebSocket-Benachrichtigungen erreichen dann jedoch weder Browser- noch Desktop-Clients. Mobile Clients verwenden separat konfigurierte Push-Benachrichtigungen.
Caddy benötigt eine kürzere Konfiguration und ruft das Zertifikat selbst ab:
vw.example.com {
reverse_proxy 127.0.0.1:8000 {
header_up X-Real-IP {remote_host}
}
}Teilen Sie dies anschließend Vaultwarden mit:
DOMAIN=https://vw.example.com
IP_HEADER=X-Real-IPIP_HEADER ist bereits standardmäßig auf X-Real-IP gesetzt. Sie müssen daher sicherstellen, dass der Proxy diesen Header tatsächlich setzt. Vaultwarden akzeptiert den Header außerdem nur von Adressen, die in IP_HEADER_TRUSTED_PROXIES aufgeführt sind. Standardmäßig ist der Wert local: jede nicht globale Adresse. Das umfasst einen Proxy auf demselben Host oder im selben Container-Netzwerk. Verbindet sich ein Proxy von einer öffentlichen Adresse, müssen Sie diese dort hinzufügen. Fehlt der Header oder stammt er von einer nicht vertrauenswürdigen Adresse, verwendet jede Logzeile und jedes Login-Rate-Limit die eigene Adresse des Proxys. Dadurch werden die Fehlversuche eines Angreifers allen Benutzern der Instanz zugerechnet. Wenn Sie stattdessen IP_HEADER=X-Forwarded-For verwenden, nimmt 1.37.4 die am weitesten rechts stehende Adresse, die kein vertrauenswürdiger Proxy ist. Listen Sie daher jeden Proxy in der Kette auf, einschließlich eines CDN vor nginx. Setzen Sie DOMAIN ebenfalls auf die tatsächlich verwendete https-URL. Vaultwarden erzeugt daraus Einladungs- und Kontowiederherstellungslinks. WebAuthn-Sicherheitsschlüssel sind außerdem an diese Origin gebunden.
Ein Detail wird häufig übersehen: Die WebSocket-Verbindung überträgt das Sitzungstoken in der Query-Zeichenfolge als /notifications/hub?access_token=[JWT]. Dadurch landet es unverschleiert im Access-Log Ihres Proxys. Redigieren Sie den Parameter access_token im Log-Format. Alternativ müssen Sie sicherstellen, dass diese Logs nicht an Orte übertragen werden, die Sie nicht kontrollieren.
Brute-Force-Angriffe am Login-Endpunkt blockieren
Ratenbegrenzungen sind standardmäßig aktiviert (LOGIN_RATELIMIT_SECONDS=60, LOGIN_RATELIMIT_MAX_BURST=10). Sie verlangsamen einen Angreifer. Sie verhindern jedoch keinen Angriff. Das übernimmt fail2ban. Dafür müssen die fehlgeschlagenen Logins von Vaultwarden an einer Stelle protokolliert werden, auf die fail2ban zugreifen kann. In Docker ist eine Logdatei der einfachste Weg. Vaultwarden schreibt standardmäßig jedoch keine solche Datei:
LOG_FILE=/data/vaultwarden.log
LOG_LEVEL=info
EXTENDED_LOGGING=trueEin fehlgeschlagener Passwort-Login erzeugt eine Zeile in diesem Format. Nach dieser Zeichenfolge muss Ihr Filter suchen:
[2026-08-07 09:14:22.000][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: 203.0.113.10. Username: user@example.com.Schreiben Sie den Filter nach /etc/fail2ban/filter.d/vaultwarden.local:
[INCLUDES]
before = common.conf
[Definition]
failregex = ^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =Und das Jail nach /etc/fail2ban/jail.d/vaultwarden.local:
[vaultwarden]
enabled = true
backend = pyinotify
port = 80,443
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /vw-data/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400Wenn Vaultwarden als systemd-Dienst und nicht in einem Container ausgeführt wird, können Sie LOG_FILE überspringen: Das fail2ban-Wiki des Projekts liest das Journal mit backend = systemd. Anstelle von logpath wird dabei ein journalmatch für die Vaultwarden-Unit verwendet.
Wenn Sie die Administrationsseite beibehalten haben, fügen Sie ein zweites Jail hinzu, dessen failregex auf ^.*Invalid admin token\. IP: <ADDR>.*$ gesetzt ist. Fehlgeschlagene Anmeldeversuche im Administrationsbereich werden mit einer anderen Meldung protokolliert. Der Login-Filter würde sie daher niemals erkennen. Prüfen Sie anschließend Ihre Konfiguration:
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwardenEin funktionierendes Jail führt Ihre Logdatei unter File list auf und meldet Currently failed: 0. Geben Sie dreimal von einem anderen Netzwerk aus ein falsches Passwort ein. Der Zähler steigt dann an. Anschließend erscheint die Adresse unter Banned IP list. Wenn sich der Zähler nie ändert, liegt das meistens an logpath: Dort muss der Pfad der Datei auf dem Host stehen, nicht der /data/...-Pfad im Container. Die zweithäufigste Ursache ist ein fehlendes oder nicht vertrauenswürdiges X-Real-IP. Dadurch richtet sich jede Sperre gegen Ihren eigenen Proxy. Die restliche Einrichtung, einschließlich des SSH-Jails, das Sie bereits betreiben sollten, finden Sie im fail2ban-Leitfaden für Ubuntu 24.04.
Das Master-Passwort schützt weiterhin das gesamte System
Clientseitige Verschlüsselung bedeutet, dass das Master-Passwort den symmetrischen Schlüssel des Benutzers schützt. Ein kurzes Master-Passwort auf einer Instanz, deren Datenbank ein Angreifer kopiert hat, wird durch nichts in diesem Beitrag geschützt, weil der Angreifer diese Kopie offline mit der Geschwindigkeit angreift, die seine Hardware ermöglicht. Keine Servereinstellung wirkt auf dem eigenen Rechner des Angreifers.
PASSWORD_ITERATIONS=600000 kann leicht falsch verstanden werden. Damit wird die Anzahl der Iterationen für den serverseitigen Hash des Login-Hashes festgelegt, den der Client sendet. Die Konfigurationsvorlage des Projekts besagt, dass ein geänderter Wert bei der nächsten Anmeldung auf bestehende Benutzer angewendet wird. Die clientseitige KDF wird dadurch nicht verändert. Diese Einstellung bestimmt, wie aufwendig ein Offline-Angriff auf den Tresor ist. Sie gehört zum jeweiligen Konto und wird im Webtresor unter Settings, Security, Keys geändert. Dabei wird der Schlüssel neu verschlüsselt. In der Dokumentation von Bitwarden steht, dass die Clients seit 2026.2.1 eine PBKDF2-Einstellung unter 600,000 Iterationen automatisch erhöhen, wenn sich der Benutzer mit dem Master-Passwort anmeldet oder den Tresor damit entsperrt. Am 10. Oktober 2026 ist dieses Verhalten für einen Vaultwarden-Server nicht bestätigt. Argon2id-Einstellungen werden dadurch nie geändert. Daher verwendet jemand, der sich vor Jahren registriert hat, möglicherweise weiterhin eine ältere, niedrigere KDF-Einstellung. Prüfen Sie das und informieren Sie die betreffende Person, weil der Server diese Änderung nicht für sie vornehmen kann.
Aktivieren Sie anschließend die Zwei-Faktor-Authentifizierung für jedes Konto. Sie schützt den Chiffretext nicht, weil sie nicht an der Verschlüsselung des Tresors beteiligt ist. Sie verhindert jedoch, dass ein gestohlenes Passwort allein für die Anmeldung und zum Synchronisieren einer Kopie ausreicht. Wenn E-Mail konfiguriert ist, macht REQUIRE_DEVICE_EMAIL=true die E-Mail zur Anmeldung eines neuen Geräts verpflichtend: Schlägt der Versand fehl, schlägt auch die Anmeldung fehl. Wenn der E-Mail-Dienst vollständig deaktiviert ist, blockiert diese Einstellung keine Anmeldungen neuer Geräte.
Backups sind der Punkt, an dem Self-Hosted-Tresore häufig ausfallen
Ein tar czf des Datenverzeichnisses, das in einem Home-Verzeichnis auf demselben VPS verbleibt, macht alle vorherigen Schritte wirkungslos. Dieses Archiv enthält db.sqlite3 mit dem Chiffrat aller Benutzer, rsa_key.pem, mit denen sich Anmeldesitzungen fälschen lassen, sowie config.json mit dem konfigurierten Wert des Admin-Tokens und dem SMTP-Passwort. Wenn Sie die Anweisungen für Argon2 befolgen, wird der Hash des Admin-Passworts gespeichert. Das SMTP-Passwort bleibt im Klartext erhalten. Der Lesezugriff auf diese eine Datei legt Kontometadaten, Servergeheimnisse und verschlüsselte Tresordaten offen.
Dafür gelten zwei Regeln. Übertragen Sie das Archiv vom Server. Verschlüsseln Sie es, bevor es den Server verlässt.
Es gibt außerdem ein Problem mit der Korrektheit. Wenn Sie db.sqlite3 mit cp kopieren, während der Dienst läuft, kann eine Datei entstehen, in die gerade geschrieben wird und die sich nicht öffnen lässt. Das bemerken Sie erst bei der Wiederherstellung. Verwenden Sie stattdessen den Snapshot von SQLite:
sqlite3 /vw-data/db.sqlite3 ".backup '/tmp/vw-db-backup.sqlite3'"Die Wiederherstellung, die als zweite Hälfte praktisch niemand testet, wird im Leitfaden zur Sicherung und Wiederherstellung von Vaultwarden behandelt.
Was Sie im Vergleich zum gehosteten Bitwarden aufgeben
Eine ehrliche Einordnung. Der gehostete Dienst von Bitwarden wird von Personen betrieben, deren Hauptaufgabe der Betrieb dieses Dienstes ist. Außerdem gibt es veröffentlichte Prüfberichte unabhängiger Dritter. Beim Self-Hosting übernehmen Sie stattdessen selbst die Verantwortung für den Patch-Rhythmus.
Vaultwarden veröffentlicht Sicherheitskorrekturen als reguläre Releases, und diese erscheinen häufig. Bei einer Prüfung am 2. November 2026 anhand des Release-Feeds des Projekts ist 1.37.5 das aktuelle Release. Es wurde am 1. November 2026 veröffentlicht. Die Release-Hinweise behandeln zwei Sicherheitshinweise mit der Einstufung Medium und empfehlen dringend, so bald wie möglich zu aktualisieren. Das Release ist außerdem erforderlich, um im Web Vault 2026.7.1 und neuer Zwei-Schritt-Anmeldemethoden zu aktivieren oder zu deaktivieren. Für 2026.9.0 und neuer ist es erforderlich, ein Konto über den Notfallzugriff zu übernehmen. Das vorherige Release 1.37.4 wurde am 5. Oktober 2026 veröffentlicht und behandelte sieben Sicherheitshinweise. Der schwerwiegendste davon war als High eingestuft. Es war das vierte Release seit 1.37.0 vom 24. Juli 2026: 1.37.1 folgte am 29. Juli, 1.37.2 am 22. August (erforderlich für Clients ab Version 2026.8.0) und 1.37.3 am 13. September. Dieses widerruft gespeicherte 2FA-Token, wenn sich Zugangsdaten oder 2FA ändern, und begrenzt die Rate der Endpunkte für Prelogin- und Authentifizierungsanfragen. Wenn Sie Organisationen mit Administratoren betreiben, denen Sie nicht vollständig vertrauen, empfehlen die Hinweise zu 1.37.4 außerdem, nach dem Update den API-Schlüssel jeder Organisation zu rotieren. 1.37.4 ändert zudem, welche Adresse aus X-Forwarded-For gelesen wird. Das wurde im Abschnitt zum Reverse Proxy weiter oben behandelt. Eine Instanz, die Sie vor einem Jahr eingerichtet und dann vergessen haben, führt Code aus, der ein Jahr alt ist. Das Tag latest hilft allein nicht: Ein laufender Container verwendet weiterhin das Image, mit dem er gestartet wurde, bis Sie docker compose pull ausführen und den Container neu erstellen. Richten Sie unbeaufsichtigte Upgrades unter Ubuntu für die Pakete des Hosts ein. Planen Sie die Aktualisierung des Containers zusätzlich in einem Kalender, dessen Erinnerungen Sie tatsächlich lesen.
Die Schlussfolgerung für eine ehrliche Bewertung lautet: Die Kryptografie basiert hier auf dem Design von Bitwarden und ist robust. Das Betriebsrisiko liegt jedoch vollständig bei Ihnen. Wenn Sie die Instanz patchen und an einem anderen Ort sichern, ist eine Vaultwarden-Instanz auf einem von Ihnen kontrollierten VPS ein sinnvoller Ort für Ihre Passwörter. Wenn Sie diese beiden Gewohnheiten nicht umsetzen werden, bezahlen Sie für den gehosteten Dienst und verwenden Sie Ihre Aufmerksamkeit für andere Aufgaben. Der Vergleich der einzelnen Funktionen steht unter Vaultwarden im Vergleich zu selbst gehostetem Bitwarden.
Den Host unterhalb des Containers härten
Vaultwarden ist ein Prozess auf einem Linux-System. root auf diesem System kann /vw-data lesen, unabhängig davon, wie die Anwendung konfiguriert ist. Führen Sie den Container mit user: "1000:1000" als nicht privilegierter Benutzer aus. Passen Sie die Eigentümer des Datenverzeichnisses entsprechend an. Binden Sie alles, was der Container nicht beschreiben muss, mit :ro schreibgeschützt ein. Sichern Sie anschließend den Zugang ab: SSH-Härtung auf einem VPS behandelt die Anmeldung ausschließlich mit Schlüsseln und die Deaktivierung der Passwortauthentifizierung. Dadurch wird der einfache Angriff verhindert, der alle bisherigen Maßnahmen umgehen kann.
FAQ
Kann jemand meine Passwörter lesen, wenn die Vaultwarden-Datenbank gestohlen wird?
Nicht direkt. Jeder Tresoreintrag wird im Client mit einem zufällig generierten Schlüssel verschlüsselt, der durch den symmetrischen Schlüssel des Benutzers oder der Organisation geschützt ist. Der aus dem Passwort abgeleitete Schlüssel schützt den symmetrischen Schlüssel des Benutzers. db.sqlite3 enthält die verschlüsselten Tresorinhalte zusammen mit Klartext-Metadaten des Kontos. Unmittelbar erbeutet werden die E-Mail-Adresse und der Passworthinweis jedes Kontos, die KDF-Einstellungen, Anmelde- und Gerätemetadaten sowie die TOTP-Geheimnisse für die Anmeldung in der Tabelle twofactor. Diese werden unverschlüsselt gespeichert, weil der Server den erwarteten Code berechnen muss. Außerdem können Angreifer den verschlüsselten Tresor beliebig lange offline angreifen. Deshalb entscheidet letztlich die Länge des Master-Passworts über das Ergebnis.
Sollte ich ADMIN_TOKEN verwenden oder die Administrationsseite vollständig deaktivieren?
Deaktivieren Sie sie, wenn möglich. Die meisten Instanzen benötigen sie einmalig, um SMTP zu konfigurieren und Benutzer einzuladen, danach nicht mehr. Setzen Sie zum Deaktivieren weder ADMIN_TOKEN noch DISABLE_ADMIN_TOKEN, entfernen Sie jeden "admin_token"-Schlüssel aus config.json und erstellen Sie den Container anschließend neu. Das Entfernen der Umgebungsvariable allein reicht nicht aus, weil die über die Administrationsseite gespeicherten Einstellungen in config.json liegen und Vorrang haben. Wenn Sie die Seite aktiviert lassen, speichern Sie das Token als von vaultwarden hash erzeugten Argon2-Hash statt als zufällige Zeichenfolge im Klartext und setzen Sie ADMIN_RATELIMIT_MAX_BURST=3.
Mein ADMIN_TOKEN ist korrekt, aber /admin weist es zurück. Was ist falsch?
Fast immer liegt es an der $-Interpolation. Eine Argon2-PHC-Zeichenfolge enthält mehrere $-Zeichen. Docker Compose expandiert diese innerhalb eines docker-compose.yml-Blocks mit environment:, sodass der Container einen veränderten Wert erhält, obwohl die Datei korrekt aussieht. Verdoppeln Sie in der Compose-Datei jedes $ zu $$ oder verschieben Sie den Wert in eine .env-Datei und setzen Sie ihn in einfache Anführungszeichen. Dort ist keine Maskierung erforderlich. Erstellen Sie den Container anschließend neu, weil Änderungen an der Umgebung durch einen Neustart nicht übernommen werden.
Muss ich Port 3012 für Benachrichtigungen weiterhin öffnen?
Nein. Die Unterstützung für WebSocket-Verkehr auf Port 3012 wurde in Vaultwarden 1.31.0 entfernt, weil Benachrichtigungen auf den Haupt-HTTP-Port umgestellt wurden. WEBSOCKET_ENABLED und WEBSOCKET_PORT sind seit 1.29.0 veraltet und werden ignoriert. Die aktuelle Einstellung ist ENABLE_WEBSOCKET, die standardmäßig auf true gesetzt ist. Schließen Sie 3012 in der Firewall und entfernen Sie den Port aus Ihrer Compose-Datei. Stellen Sie anschließend sicher, dass Ihr Reverse Proxy die Header Upgrade und Connection weiterleitet, weil Browser- und Desktop-Benachrichtigungen über WebSocket davon abhängen. Mobile Clients verwenden separat konfigurierte Push-Benachrichtigungen.