Schwache Kodierung für Passwort
Beschreibung
Schwache Kodierung für Passwort ist eine Schwachstelle, die auftritt, wenn ein Produkt Passwörter mit trivialen Kodierungsmethoden wie Base64, ROT13, XOR mit einem festen Schlüssel oder einfacher Zeichensubstitution verschleiert, anstatt ordnungsgemäßen kryptografischen Schutz anzuwenden. Kodierung transformiert Daten in eine andere Darstellung, ist aber so konzipiert, dass sie von jedem, der das Kodierungsschema kennt, reversibel ist – sie bietet keine Sicherheit gegen entschlossene Angreifer. Wenn Passwörter nur kodiert statt ordnungsgemäß gehasht oder verschlüsselt werden, kann jeder, der Zugang zu den kodierten Daten erhält, sie leicht dekodieren, um das ursprüngliche Klartext-Passwort abzurufen. Dies erzeugt ein falsches Sicherheitsgefühl, während die Anmeldedaten grundlegend ungeschützt bleiben.
Risiko
Die Verwendung schwacher Kodierung für Passwörter erzeugt schwere Sicherheitsrisiken, da der Schutz illusorisch ist. Base64 und ähnliche Kodierungen sind standardisiert, gut dokumentiert und mit frei verfügbaren Tools sofort reversibel. Angreifer, die Zugang zu Konfigurationsdateien, Datenbanken oder Speicherauszügen mit kodierten Passwörtern erhalten, können sie innerhalb von Sekunden dekodieren. Das Risiko wird verstärkt, wenn Entwickler fälschlicherweise glauben, dass Kodierung Sicherheit bietet, und daher schwächere Zugriffskontrollen auf kodierte Anmeldedatenspeicher implementieren. Anders als beim Hashing bewahrt Kodierung das ursprüngliche Passwort, was bedeutet, dass ein einziger Breach alle betroffenen Anmeldedaten in ihrer ursprünglichen Form offenlegt. Dies ermöglicht Credential-Stuffing-Angriffe, wenn Benutzer Passwörter über Systeme hinweg wiederverwenden.
Lösung
Verwenden Sie niemals Kodierung (Base64, Hex, URL-Kodierung usw.) als Passwortschutzmechanismus. Für Passwortspeicherung verwenden Sie starke Einweg-Hashing-Algorithmen, die speziell für Passwörter konzipiert sind, wie Argon2id, bcrypt oder scrypt, mit angemessenen Kostenfaktoren. Für Passwörter, die wiederherstellbar sein müssen (wie zur Verbindung mit externen Diensten), verwenden Sie starke Verschlüsselung mit Schlüsseln von mindestens 128 Bit Länge und speichern Sie den Verschlüsselungsschlüssel getrennt vom verschlüsselten Passwort. Implementieren Sie ordnungsgemäße Schlüsselverwaltungspraktiken einschließlich Schlüsselrotation und Zugriffskontrollen. Wenn Legacy-Systeme kodierte Passwörter verwenden, planen Sie die Migration zu ordnungsgemäßem kryptografischem Schutz. Verwenden Sie statische Analysetools, um Kodierungsfunktionen zu erkennen, die auf Passwortvariablen angewendet werden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Angreifer, die Zugang zu kodierten Passwörtern erhalten, können sie leicht dekodieren, um Klartext-Anmeldedaten zu erhalten. Dies ermöglicht Privilegieneskalation, Identitätsannahme und unbefugten Zugriff auf geschützte Ressourcen. |
Beispielcode
Anfälliger Code (Java)
Der folgende Code demonstriert anfällige Passwortkodierungsmuster:
import java.util.Base64;
import java.util.Properties;
public class VulnerablePasswordStorage {
// Anfällig: Base64-Kodierung für Passwort-"Schutz"
public void storePasswordVulnerable(String username, String password) {
// Base64-Kodierung bietet NULL Sicherheit
String encoded = Base64.getEncoder().encodeToString(password.getBytes());
Properties props = new Properties();
props.setProperty(username + ".password", encoded);
// In Datei speichern...
// Jeder kann dekodieren:
// echo "UGFzc3dvcmQxMjM=" | base64 -d
// Ergebnis: Password123
}
// Anfällig: XOR-"Verschlüsselung" mit festem Schlüssel
public String xorEncode(String password, String key) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < password.length(); i++) {
result.append((char)(password.charAt(i) ^ key.charAt(i % key.length())));
}
// Dies ist mit derselben Operation trivial reversibel
return Base64.getEncoder().encodeToString(result.toString().getBytes());
}
// Anfällig: ROT13-"Verschleierung"
public String rot13(String password) {
StringBuilder result = new StringBuilder();
for (char c : password.toCharArray()) {
if (c >= 'a' && c <= 'z') {
c = (char)(((c - 'a' + 13) % 26) + 'a');
} else if (c >= 'A' && c <= 'Z') {
c = (char)(((c - 'A' + 13) % 26) + 'A');
}
result.append(c);
}
return result.toString(); // ROT13 zweimal anwenden = Original
}
// Anfällig: Hex-Kodierung
public String hexEncode(String password) {
StringBuilder hex = new StringBuilder();
for (byte b : password.getBytes()) {
hex.append(String.format("%02x", b));
}
return hex.toString(); // Trivial reversibel
}
}
// Anfälliges C#-Beispiel - Base64 in Registry
using Microsoft.Win32;
using System;
public class VulnerableCredentialStore
{
// Anfällig: Base64-kodiertes Passwort in Registry
public void StoreCredentials(string username, string password)
{
// Base64 ist KEINE Verschlüsselung!
string encodedPassword = Convert.ToBase64String(
System.Text.Encoding.UTF8.GetBytes(password)
);
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp");
key.SetValue("Username", username);
key.SetValue("Password", encodedPassword); // Leicht dekodierbar
key.Close();
}
}
Korrigierter Code (Java)
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.security.SecureRandom;
public class SecurePasswordStorage {
// Korrigiert: Ordnungsgemäßes Passwort-Hashing für Speicherung
public String hashPassword(String password) {
// bcrypt, Argon2 oder PBKDF2 verwenden
int iterations = 100000;
int keyLength = 256;
byte[] salt = generateSalt();
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
iterations,
keyLength
);
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
// Salt + Hash zusammen speichern
return Base64.getEncoder().encodeToString(salt) + ":" +
Base64.getEncoder().encodeToString(hash);
} finally {
spec.clearPassword();
}
}
public boolean verifyPassword(String password, String storedHash) {
String[] parts = storedHash.split(":");
byte[] salt = Base64.getDecoder().decode(parts[0]);
byte[] expectedHash = Base64.getDecoder().decode(parts[1]);
// Bereitgestelltes Passwort mit demselben Salt hashen
byte[] actualHash = hashWithSalt(password, salt);
// Konstantzeit-Vergleich
return MessageDigest.isEqual(expectedHash, actualHash);
}
// Korrigiert: Ordnungsgemäße Verschlüsselung für wiederherstellbare Anmeldedaten
public byte[] encryptCredential(String credential, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] encrypted = cipher.doFinal(credential.getBytes());
// IV den verschlüsselten Daten voranstellen
byte[] result = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);
return result;
}
private byte[] generateSalt() {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
return salt;
}
}
Die Korrektur verwendet ordnungsgemäßes Passwort-Hashing (PBKDF2, bcrypt oder Argon2) für Passwortspeicherung und authentifizierte Verschlüsselung (AES-GCM) für Anmeldedaten, die wiederherstellbar sein müssen.
Ausgenutzt in der Praxis
Jenkins Anmeldedaten-Kodierung (Jenkins, Mehrere CVEs)
Mehrere Versionen von Jenkins speicherten Anmeldedaten mit Base64-Kodierung, die von Benutzern mit Zugang zu Konfigurationsdateien leicht dekodiert wurde. Während spätere Versionen ordnungsgemäße Verschlüsselung implementierten, enthielten viele Legacy-Installationen und Backup-Dateien leicht dekodierbare Anmeldedaten, die Angreifer während der Post-Kompromittierungs-Aufklärung ausnutzten.
Router-Firmware Base64-Passwörter (Mehrere Hersteller, Fortlaufend)
Zahlreiche Router- und IoT-Gerätehersteller haben administrative Passwörter mit Base64-Kodierung in Firmware-Images und Konfigurationsdateien gespeichert. Sicherheitsforscher extrahieren und dekodieren diese Anmeldedaten regelmäßig, veröffentlichen sie in Schwachstellenoffenlegungen und ermöglichen Angriffe gegen bereitgestellte Geräte.
Quellcode-Repository kodierte Anmeldedaten (GitHub/GitLab, Fortlaufend)
Entwickler committen häufig Base64-kodierte Anmeldedaten in Versionskontrollsysteme, da sie glauben, die Kodierung biete Schutz. Automatisierte Scanning-Tools erkennen und dekodieren diese Anmeldedaten, was zu weit verbreiteter Kompromittierung von API-Schlüsseln, Datenbank-Passwörtern und Service-Konten führt.
Tools zum Testen/Ausnutzen
-
CyberChef — Web-basiertes Tool zum sofortigen Dekodieren von Base64, Hex, ROT13 und anderen Kodierungsschemata.
-
Gitleaks — Erkennt kodierte Secrets in Repositories und kann gängige Kodierungsmuster identifizieren.
-
Base64 Kommandozeile — Standard-Unix-Tool zum Kodieren/Dekodieren von Base64, demonstriert triviale Reversibilität.
CVE-Beispiele
-
CVE-2019-10352 — Anwendung speicherte Passwörter mit Base64-Kodierung, die leicht dekodiert wurde.
-
CVE-2018-10936 — PostgreSQL JDBC-Treiber übertrug Base64-kodierte Passwörter in Logdateien.
-
CVE-2020-5410 — Spring Cloud Config Server legte Base64-kodierte Secrets in der Konfiguration offen.
Referenzen
-
MITRE Corporation. "CWE-261: Weak Encoding for Password." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/261.html
-
OWASP Foundation. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
-
NIST. "Digital Identity Guidelines: Authentication and Lifecycle Management." SP 800-63B. https://pages.nist.gov/800-63-3/sp800-63b.html