Zugangsdaten in URLs im Log unkenntlich machen - #3780
Conversation
Ein Passwort, das als URL-userinfo übergeben wird (schema://benutzer:passwort@host/), steht im Klartext im Log. RedactingFilter greift nicht, weil alle drei REDACTION_PATTERNS an einem Feldnamen ansetzen und die userinfo einer URL nach RFC 3986 keinen hat. Mehrere SoC-Module - json, http, homeassistant, psacc - bieten ein freies url-Feld und keine getrennten Felder für Benutzer und Passwort. Für einen Endpunkt hinter Basic Auth ist es der einzige Weg, die Zugangsdaten in die URL zu schreiben. Zwei Stellen protokollieren eine solche URL: - packages/modules/vehicles/json/soc.py gibt die konfigurierte URL auf Debug-Level aus, das landet in soc.log - packages/helpermodules/subdata.py protokolliert jede MQTT-Nachricht samt Payload, damit landet die Modulkonfiguration selbst in mqtt.log An beiden Handlern hängt bereits ein RedactingFilter. Die Absicht, Geheimnisse aus diesen Dateien herauszuhalten, ist also vorhanden - es fehlte lediglich das Muster für diese Form. Das neue Muster wirkt in redact_sensitive_info() und damit für jeden Handler, an dem bereits ein RedactingFilter hängt. Der Benutzername bleibt sichtbar, ersetzt wird nur das Passwort: Bei der Diagnose eines Authentifizierungsproblems ist die Auskunft, mit welchem Benutzer verbunden wurde, der nützliche Teil der Logzeile. Der Wertteil ist bewusst greedy. Enthält das Passwort selbst ein @, reicht der Treffer bis zum letzten @ vor dem Host statt nur bis zum ersten. In allen übrigen Fällen sind beide Varianten identisch. Schrägstrich und Whitespace bleiben ausgeschlossen, dadurch kann ein Treffer nie über die Authority hinaus in Pfad oder Query laufen - eine Mailadresse in einem Query-Parameter bleibt unangetastet, was ein Test abdeckt. Die beiden Excepthooks schreiben über open()/write() in thread_errors.log und umgehen damit das Logging-Framework und den Filter. Eine ungefangene Exception, die eine URL mit sich trägt - etwa ein InvalidURI aus einem Verbindungsaufruf -, landet dort im Klartext. Sie führen ihren Text jetzt durch dieselbe Funktion; aus print_tb() wurde format_tb(), weil sich nur ein String filtern lässt, die Ausgabe bleibt unverändert.
There was a problem hiding this comment.
Pull request overview
Diese PR erweitert die bestehende Log-Redaction in helpermodules.logger, damit Passwörter in URLs (RFC 3986 userinfo, z. B. scheme://user:password@host/) nicht mehr im Klartext in Logdateien auftauchen, und ergänzt dazu Tests.
Changes:
- Neues Redaction-Pattern für URL-Credentials, das nur das Passwort ersetzt und den Benutzernamen sichtbar lässt.
- Excepthook-Ausgaben für
thread_errors.logwerden (teilweise) durchredact_sensitive_info()geleitet. - Neue Pytest-Testfälle für URL-Credential-Redaction und Nicht-Regressionen (URLs ohne Credentials,
@im Query, Mailadresse).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/helpermodules/logger.py | Ergänzt URL-Credentials-Redaction und nutzt Redaction in Excepthook-Ausgaben. |
| packages/helpermodules/logger_test.py | Fügt Parametrized-Tests für URL-Credentials-Redaction und Non-Matches hinzu. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| f.write("Uncaught exception:\n") | ||
| f.write(f"Type: {exc_type}\n") | ||
| f.write(f"Value: {exc_value}\n") | ||
| f.write(redact_sensitive_info(f"Value: {exc_value}\n")) | ||
| f.write(f"Traceback:{exc_traceback}\n") |
There was a problem hiding this comment.
exc_traceback ist ein Traceback-Objekt, kein formatierter Text. Die Interpolation ergibt nur Traceback:<traceback object at 0x7f...>, unabhängig davon, was die Exception enthält. Es gibt hier also nichts zu redigieren.
Der Wert, der Zugangsdaten enthalten kann, ist exc_value – der geht eine Zeile darüber durch redact_sensitive_info().
Davon abgesehen ist die Zeile in ihrer jetzigen Form wenig hilfreich: In thread_errors.log landet eine Objektadresse statt des Stacktrace. threading_excepthook macht es darüber mit traceback.format_tb() richtig. Das ist aber bestehendes Verhalten und nicht Gegenstand dieses PR – ich lasse es hier bewusst unverändert.
Problem
Ein Passwort, das als URL-userinfo übergeben wird (
schema://benutzer:passwort@host/), steht im Klartext im Log.RedactingFiltergreift nicht: Alle dreiREDACTION_PATTERNSsetzen an einem Feldnamen an (password=…,"password": "…"), und die userinfo einer URL nach RFC 3986 hat keinen.Das ist kein Randfall. Mehrere SoC-Module —
json,http,homeassistant,psacc— bieten ein freiesurl-Feld und keine getrennten Felder für Benutzer und Passwort. Für einen Endpunkt hinter Basic Auth ist es der einzige Weg, die Zugangsdaten in die URL zu schreiben, undrequestsunterstützt genau das.Zwei Stellen protokollieren eine solche URL:
packages/modules/vehicles/json/soc.pygibt die konfigurierte URL auf Debug-Level aus →soc.logpackages/helpermodules/subdata.pyprotokolliert jede MQTT-Nachricht samt Payload, damit landet die Modulkonfiguration selbst inmqtt.logAn beiden Handlern hängt bereits ein
RedactingFilter. Die Absicht, Geheimnisse aus diesen Dateien herauszuhalten, ist also vorhanden — es fehlte lediglich das Muster für diese Form.Reproduktion
Ein JSON-SoC-Modul mit
https://benutzer:geheim@nas.local/soc.jsonkonfiguriert und Debug-Logging aktiviert, ergibt auf dem aktuellen master:Mit dieser Änderung steht in beiden Dateien
https://benutzer:***REDACTED***@nas.local/soc.json.Änderung
Ein Muster mehr in
redact_sensitive_info(), damit wirkt es für jeden Handler, an dem bereits einRedactingFilterhängt:Der Benutzername bleibt sichtbar, ersetzt wird nur das Passwort. Bei der Diagnose eines Authentifizierungsproblems ist die Auskunft, mit welchem Benutzer verbunden wurde, der nützliche Teil der Logzeile.
Der Wertteil ist bewusst greedy: Enthält das Passwort selbst ein
@, reicht der Treffer bis zum letzten@vor dem Host statt nur bis zum ersten. In allen übrigen Fällen sind beide Varianten identisch. Schrägstrich und Whitespace bleiben ausgeschlossen, dadurch kann ein Treffer nie über die Authority hinaus in Pfad oder Query laufen — eine Mailadresse in einem Query-Parameter bleibt unangetastet.thread_errors.log
Die beiden Excepthooks in
setup_logging()schreiben überopen()/write()und umgehen damit das Logging-Framework und den Filter. Eine ungefangene Exception, die eine URL mit sich trägt — etwa einInvalidURIaus einem Verbindungsaufruf —, landet dort im Klartext. Sie führen ihren Text jetzt durch dieselbe Funktion. Ausprint_tb()wurdeformat_tb(), weil sich nur ein String filtern lässt; die Ausgabe bleibt unverändert.Tests
Neu ist
packages/helpermodules/logger_test.pymit zehn Fällen, darunter drei, die unverändert bleiben müssen: eine URL ohne Zugangsdaten, ein@in einem Query-Parameter und eine Mailadresse hinter einer URL.Ein dokumentierter Randfall: Heißt der Benutzer wie ein Eintrag aus
KNOWN_SENSITIVE_FIELDS(token,secret, …), greift zusätzlich das bestehende Feldmuster und kürzt die URL aufws://token=***REDACTED***, weil dessen Wertmuster[^\s&]+auch/und@erlaubt. Das Passwort ist auch in diesem Fall entfernt, es fehlen lediglich Host und Pfad in der Logzeile. Das Verhalten ist von der Reihenfolge der beiden Ersetzungen unabhängig und durch einen Test festgehalten.