major
#29849
Configuration: an encrypted property value that is not a valid encrypted value is silently "decrypted" to garbage instead of being rejected
Kontext
Werte von Konfigurations-Properties mit @Encrypted (z.B. Passwörter von Datenbank-, Mail- oder OpenAPI-Zugängen) werden verschlüsselt in der Konfiguration abgelegt und beim Lesen über ConfigurationEncryption.decrypt entschlüsselt (EncodingConfigurationValueProvider, XMLProperties.decryptValue). Ein Wert mit dem Präfix unencrypted: wird unverschlüsselt übernommen.
Problem
Wird versehentlich ein Klartext-Wert ohne unencrypted: konfiguriert (typisch: ein Passwort aus einer Umgebungsvariablen über einen Alias), dann wird das in den meisten Fällen nicht erkannt:
- Die Verschlüsselung AES/OFB/NoPadding (SymmetricEncryption über Base64Encoder) ist ein Stromchiffre-Modus ohne Integritätsprüfung. Jede Bytefolge, die mindestens so lang ist wie der IV (16 Byte), lässt sich "entschlüsseln".
- Base64.decodeBase64 überspringt Zeichen außerhalb des Base64-Alphabets stillschweigend, so dass auch Werte mit Sonderzeichen dekodiert werden.
- Das Ergebnis wird mit Ersetzungszeichen in einen String umgewandelt, ungültiges UTF-8 fällt also ebenfalls nicht auf.
Nur wenn der Base64-dekodierte Wert kürzer als 16 Byte ist, scheitert das Lesen mit einem Konfigurationsfehler (siehe #29788). Ein längerer Klartext-Wert wird stillschweigend zu einem unsinnigen Wert, und die Anwendung scheitert erst später mit einer irreführenden Meldung (z.B. Login an der Datenbank oder am Mailserver abgelehnt). Die eigentliche Ursache, ein fehlendes unencrypted: oder ein unvollständig kopierter verschlüsselter Wert, ist daraus nicht zu erkennen.
Lösung
Authentifiziertes Format für neue Werte
- ConfigurationEncryption.encrypt schreibt Werte in einem neuen Format mit authentifizierter Verschlüsselung (AES/GCM/NoPadding, 128-Bit-Tag). Der Wert erhält die Versionskennung {gcm} als Präfix, gefolgt von URL-sicherem Base64 aus IV, Chiffretext und Tag.
- ConfigurationEncryption.decrypt erkennt das Format am Präfix. Ein Wert im neuen Format, dessen Tag-Prüfung scheitert (Klartext, abgeschnittener oder veränderter Wert), führt zuverlässig zu einem Konfigurationsfehler, der den Property-Namen nennt, aber nicht den Wert (wie in #29788).
- Das Base64 des neuen Formats wird strikt dekodiert: Zeichen außerhalb des Alphabets sind ein Fehler.
Bisheriges Format weiter lesbar
- Werte ohne Präfix werden weiterhin mit dem bisherigen Verfahren (AES/OFB/NoPadding) gelesen, damit bestehende Konfigurationen ohne Migration funktionieren.
- Für diese Werte werden zusätzlich Plausibilitätsprüfungen eingeführt, die ohne Formatänderung möglich sind: striktes Base64-Alphabet (ein Wert mit Zeichen wie !`, `#, @` oder Leerzeichen ist kein verschlüsselter Wert) und striktes UTF-8 beim Umwandeln des Ergebnisses (`CodingErrorAction.REPORT). Diese Prüfungen sind nicht vollständig (ein kurzer alphanumerischer Klartext kann sie zufällig bestehen), fangen aber die häufigsten Fälle ab.
Warnung für Werte im bisherigen Format
- Beim Lesen eines Werts im bisherigen Format wird eine Warnung protokolliert, die nicht den Wert nennt, mit dem Hinweis, den Wert im neuen Format neu zu verschlüsseln. Bei typisierter Konfiguration (EncodingConfigurationValueProvider) nennt sie den Property-Namen, bei XMLProperties den Schlüssel und die Stelle in der Konfigurationsdatei.
- Damit lassen sich die Werte schrittweise umstellen. Das bisherige Format kann in einem späteren Release entfallen; erst dieser Schritt erfordert eine Migration.
Technische Hinweise
- SymmetricEncryption wird auch vom EncryptionService verwendet (Signaturen, EncryptingTransformer für verschlüsselte Datenbankinhalte). Dessen Verhalten und Datenformat ändern sich nicht; das authentifizierte Verfahren ist eine eigene Klasse AuthenticatedEncryption neben SymmetricEncryption und wird nur von ConfigurationEncryption verwendet.
- Der Schlüssel von ConfigurationEncryption ist eine fest im Quelltext hinterlegte Passphrase. Die Verschlüsselung schützt Konfigurationswerte daher nur vor beiläufigem Lesen, nicht vor jemandem, der den (öffentlichen) Quelltext kennt. Dieses Ticket ändert daran nichts; es geht um die zuverlässige Erkennung ungültiger Werte.
- Die Wartungsseite EncodeConfigurationValues.jsp, mit der Werte für die Konfiguration verschlüsselt werden, verwendet ConfigurationEncryption.encrypt und erzeugt damit das neue Format.