Datei hochladen
  1. base64hub
  2. Base64 verstehen
  3. Kodierung vs. Verschlüsselung
Sicherheit

Base64 ist keine Verschlüsselung: Kodierung vs. Verschlüsselung

Base64 ändert nur, wie Bytes geschrieben werden. Alles, was Sie in Base64 kodieren, kann jeder dekodieren, der es sieht, mit einem einzigen Befehl und ohne Schlüssel.

Drei verschiedene Operationen

Base64 taucht regelmäßig an Stellen auf, an denen Leute Schutz erwarten, daher lohnt es sich, genau zu sein, was es tut. Es ist eine Kodierung: eine feste, öffentliche Abbildung von Bytes auf Text (siehe So funktioniert Base64). Die Abbildung hat keinen Schlüssel und kein Geheimnis, und dieselbe Eingabe ergibt immer dieselbe Ausgabe.

Kodierung (Base64, Hex)Verschlüsselung (AES-GCM)Hashing (SHA-256, Argon2)
ZweckBytes als Text darstellenDaten vertraulich haltenDaten mit Fingerabdruck versehen oder prüfen
UmkehrbarJa, von jedemJa, mit dem SchlüsselNein
Braucht einen SchlüsselNeinJaNein (HMAC verwendet einen)
Gleiche Eingabe, gleiche AusgabeImmerNein, mit zufälliger NonceJa, außer mit Salt
AusgabegrößeEtwa 4/3 der EingabeEingabe plus Nonce und TagFeste Länge

Das Dekodieren kostet keinerlei Mühe:

echo ZGVwbG95Omh1bnRlcjI= | base64 -d
# deploy:hunter2

Für jemanden, der kurz darauf schaut, wirkt diese Zeichenkette undurchsichtig. Für ein Skript, einen Log-Scanner oder einen Angreifer ist sie Klartext mit einem zusätzlichen Schritt.

Ein brauchbarer Test: Wäre ein Wert auf einem Screenshot ein Problem, ist er es nach der Base64-Kodierung immer noch.

Wie Base64-Daten in der Praxis durchsickern

Das Muster ist immer dasselbe: Ein Wert wird für den Transport Base64-kodiert, jemand hält ihn für geschützt, und dann landet er irgendwo, wo er gelesen werden kann. Das sind die üblichen Formen dieses Fehlers.

HTTP-Basic-Auth-Header

Die Basic-Authentifizierung (RFC 7617) sendet Benutzername und Passwort bei jeder Anfrage als Base64(user:password):

Authorization: Basic ZGVwbG95Omh1bnRlcjI=

Der einzige Schutz ist TLS auf der Verbindung. Sobald der Header diesen Tunnel verlässt, liegt das Passwort praktisch im Klartext vor. Typische Wege dorthin: Reverse-Proxys oder Anwendungsframeworks, die Anfrage-Header protokollieren, HAR-Dateien, die aus den Entwicklertools des Browsers exportiert und an ein Support-Ticket gehängt werden, Ausgaben von curl -v, die in einen Chat kopiert werden, und Fehler-Tracking-Tools, die vollständige Anfragen erfassen. Der Basic-Auth-Decoder zeigt, wie wenig es braucht, um einen solchen Header zu lesen.

Kubernetes Secrets

Ein Kubernetes Secret speichert seine Werte Base64-kodiert:

apiVersion: v1
kind: Secret
metadata:
  name: db
data:
  password: czNjcjN0

czNjcjN0 ist s3cr3t. Die Kodierung gibt es, damit Werte Binärdaten in YAML aufnehmen können. Die Kubernetes-Dokumentation sagt ausdrücklich, dass Secrets standardmäßig unverschlüsselt in etcd gespeichert werden und jeder, der das Secret über die API mit get abrufen darf, es lesen kann. Die häufigsten Lecks sind Manifeste mit echten data:-Werten in einem Git-Repository, Ausgaben von kubectl get secret -o yaml in Tickets und unverschlüsselt abgelegte Backups von etcd oder des Clusterzustands. Der Kubernetes-Secret-Decoder dekodiert alle Werte eines Secrets auf einmal, so prüfen Sie auch schnell, was ein Manifest tatsächlich preisgibt.

Zugangsdaten für Container-Registrys folgen demselben Muster. Das Feld auth in einer Docker-config.json oder einem .dockerconfigjson-Secret ist Base64(user:password); der dockerconfigjson-Decoder liest es.

JWT-Payloads

Ein signiertes JWT sieht aus wie ein versiegeltes Token, seine ersten beiden Teile sind aber nur JSON in Base64URL:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImVtYWlsIjoiYW5hQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIn0.<signature>

Die Payload ergibt dekodiert {"sub":"42","email":"ana@example.com","role":"admin"}. Die Signatur schützt das Token vor Veränderung; für die Vertraulichkeit tut sie nichts. Jede Browsererweiterung, jeder Proxy, jede Logdatei und jedes Analyse-Skript, das das Token sieht, kann lesen, was darin steht. Personenbezogene Daten, interne IDs, die Sie lieber nicht preisgeben, und alles Geheime gehören nicht in eine JWT-Payload. Müssen sie in einem Token reisen, nehmen Sie ein verschlüsseltes JWT (JWE). Mehr dazu unter Base64 in JWT.

„Verschleierte“ Secrets in Code und Konfiguration

API-Schlüssel, die als Base64 in Frontend-JavaScript, App-Bundles oder Konfigurationsdateien liegen, sind ein Klassiker. Die übliche Begründung lautet, so sei der Schlüssel nicht „im Klartext“. Er wird dadurch tatsächlich von einer naiven Suche nach dem wörtlichen Schlüssel nicht gefunden, und das ist alles. Was an einen Client ausgeliefert wird, kann dieser Client lesen.

Dieselbe Idee läuft bei Angriffen in umgekehrter Richtung. PowerShells -EncodedCommand nimmt Base64 von UTF-16LE-Text entgegen und wird oft genutzt, um bösartige Befehle auf den ersten Blick schwerer lesbar zu machen. Verteidiger dekodieren das in Sekunden, etwa mit dem Decoder für PowerShell-EncodedCommand.

Was Sie stattdessen verwenden sollten

Wählen Sie das Werkzeug nach der Aufgabe. Base64 hat in jedem dieser Fälle weiterhin seinen Platz, als Textverpackung für Bytes, die durch etwas anderes geschützt sind.

Für vertrauliche Daten: authentifizierte Verschlüsselung

Nutzen Sie AES-256-GCM oder ChaCha20-Poly1305 über eine gut gepflegte Bibliothek. Beide sind authentifiziert, Manipulationen werden beim Entschlüsseln also erkannt. Zwei Regeln sind wichtiger als der Algorithmus: Verwenden Sie eine Nonce nie zweimal mit demselben Schlüssel, und bewahren Sie den Schlüssel nicht neben den Daten auf (sondern in einem KMS, einem Secrets-Manager oder einer Umgebung, die der Angreifer nicht ebenfalls kontrolliert).

import { randomBytes, createCipheriv, createDecipheriv } from "node:crypto";

const key = randomBytes(32);   // in real code, load this from a KMS or secret store
const iv = randomBytes(12);    // 96-bit nonce, unique per message

const cipher = createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([cipher.update("deploy:hunter2", "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();

// Base64 carries the encrypted bytes as text: nonce + tag + ciphertext
const token = Buffer.concat([iv, tag, ct]).toString("base64");

const raw = Buffer.from(token, "base64");
const d = createDecipheriv("aes-256-gcm", key, raw.subarray(0, 12));
d.setAuthTag(raw.subarray(12, 28));
console.log(Buffer.concat([d.update(raw.subarray(28)), d.final()]).toString("utf8"));
// deploy:hunter2

Die Ausgabe ist eine Base64-Zeichenkette mit 56 Zeichen, die sich bei jedem Durchlauf ändert, weil die Nonce zufällig ist. Ohne den Schlüssel ist sie nutzlos. Wer ohne Code experimentieren will, nimmt die Tools AES verschlüsseln und AES entschlüsseln, die in Ihrem Browser laufen.

Für gespeicherte Passwörter: ein langsamer Hash mit Salt

Passwörter sollten weder verschlüsselt noch kodiert werden. Speichern Sie einen Hash aus einer Funktion, die für Passwörter gemacht ist: Argon2id, scrypt oder bcrypt, mit einem eigenen zufälligen Salt pro Passwort. Ein einfacher SHA-256 eines Passworts ist zu schnell berechnet und fällt Brute-Force-Angriffen zum Opfer. Pythons Standardbibliothek enthält scrypt:

import hashlib, hmac, os

def hash_password(pw: str) -> bytes:
    salt = os.urandom(16)
    return salt + hashlib.scrypt(pw.encode(), salt=salt, n=2**14, r=8, p=1)

def check_password(pw: str, stored: bytes) -> bool:
    salt, digest = stored[:16], stored[16:]
    candidate = hashlib.scrypt(pw.encode(), salt=salt, n=2**14, r=8, p=1)
    return hmac.compare_digest(candidate, digest)

Müssen Sie das Ergebnis in eine Textspalte oder in JSON schreiben, kodieren Sie es in Base64.

Für den Nachweis der Integrität: Signaturen und HMAC

Wenn das Ziel „niemand hat das verändert“ lautet und nicht „niemand kann das lesen“, nehmen Sie einen HMAC mit gemeinsamem Secret oder eine digitale Signatur. Das Ergebnis sind Bytes, meist als Base64 oder Hex transportiert. Der HMAC-Generator zeigt die Ausgabe in beiden Formen.

Für Zugangsdaten bei der Übertragung und im Ruhezustand

Verwenden Sie TLS für jede Verbindung, die Basic Auth oder Tokens transportiert, ziehen Sie kurzlebige Tokens statischen Passwörtern vor, entfernen Sie Authorization-Header aus Logs, aktivieren Sie die Verschlüsselung im Ruhezustand für Kubernetes Secrets, und halten Sie echte Secret-Werte aus Git heraus.

Häufige Fragen

Ist Base64 sicher?

Base64 hat keinerlei Sicherheitseigenschaften. Es gibt keinen Schlüssel, zum Dekodieren braucht man also nichts außer der Zeichenkette selbst. Als Transportformat für Daten, die bereits geschützt sind, etwa Chiffretext oder eine Signatur, ist es unbedenklich.

Reicht Base64, um ein Passwort in einer Konfigurationsdatei zu verstecken?

Nein. Jeder, der die Datei lesen kann, dekodiert das Passwort mit base64 -d. Schränken Sie die Dateirechte ein, nutzen Sie einen Secrets-Manager oder verschlüsseln Sie den Wert mit einem Schlüssel, der woanders liegt.

Warum sind Kubernetes Secrets Base64-kodiert, wenn es nicht um Sicherheit geht?

Damit Secret-Werte beliebige Binärdaten wie TLS-Schlüssel oder Keystores in YAML und JSON aufnehmen können. Den Schutz liefern RBAC, Verschlüsselung von etcd im Ruhezustand und ein sorgfältiger Umgang mit Manifesten.

Kann jeder die Payload eines JWT ohne das Secret lesen?

Ja. Header und Payload sind JSON in Base64URL, das jeder lesen kann. Der Schlüssel wird nur gebraucht, um die Signatur zu prüfen oder zu erstellen. Probieren Sie es mit dem JWT-Decoder aus.

Was ist der Unterschied zwischen Kodierung, Verschlüsselung und Hashing?

Kodierung kann jeder umkehren. Verschlüsselung lässt sich nur mit einem Schlüssel umkehren. Hashing ist eine Einbahnstraße: Man kann einen Wert gegen einen Hash prüfen, den Hash aber nicht zurück in den Wert verwandeln.

Weiterlesen