Blog

Personenbezogene Daten aus Exception-Messages fernhalten, bevor sie die App verlassen

QueueHawk behandelt Job-Payloads bereits konservativ: die tatsächlichen Argumentwerte eines Hangfire-Jobs werden nie gesendet, ausser man aktiviert das explizit über IncludeJobPayloads. Aber ein fehlgeschlagener Job trägt nicht nur seine Argumente in sich — er trägt auch, was die Exception selbst sagt, und eine Exception-Message ist Freitext, geschrieben von welchem Code auch immer (eigenem, einer Library, einem Datenbank-Treiber) sie geworfen hat. Gelegentlich steht dort etwas, das dort nicht stehen sollte.

Wo das tatsächlich auftritt

Eine Validierungs-Exception, die den abgelehnten Wert zitiert ("Invalid email: jane.doe@client.com"). Eine Unique-Constraint-Verletzung des Datenbank-Treibers, die den kollidierenden Schlüssel der Zeile wiedergibt. Ein Third-Party-SDK, das eine Request-URL samt Query-String in seiner Exception-Message mitliefert. Nichts davon ist ein Bug in QueueHawk oder Hangfire — so funktionieren Exceptions einfach: die Message ist, was der werfende Code dort hineingeschrieben hat, und die meisten Codes wurden nicht mit dem Gedanken "dieser Text könnte den Prozess verlassen" geschrieben.

Job-Payloads und Exception-Inhalte sind zwei unterschiedliche Probleme mit zwei unterschiedlichen Defaults, bewusst so: Payloads sind Opt-in, weil es selten einen Diagnosegrund gibt, sie überhaupt zu sehen. Exception-Messages und Stacktraces werden standardmässig gesendet (nur längen-gekürzt via MaxStackTraceLength), weil sie meist der eigentliche Grund sind, warum man überhaupt monitort — ein Alert ohne Exception-Detail ist kaum ein Alert.

Exception-Inhalte zensieren mit OnBeforeSend

Für die Fälle, in denen der Exception-Text selbst bereinigt werden muss, bietet QueueHawk.Agent einen OnBeforeSend-Callback auf QueueHawkOptions. Er läuft für jedes erfasste State-Change-Event, bevor dieses Event überhaupt in den lokalen In-Memory-Puffer des Agenten geschrieben wird — ein zensiertes Feld liegt dadurch nie unzensiert im Speicher des eigenen Prozesses, geschweige denn geht es je über das Netzwerk raus.

Program.cs
builder.Services.AddQueueHawk(options =>
{
    options.ApiKey = builder.Configuration["QueueHawk:ApiKey"];
    options.Environment = "Production";

    options.OnBeforeSend = jobEvent =>
    {
        // Nur Failed-Events tragen Exception-Inhalte - alle anderen laufen unverändert durch.
        if (jobEvent.ExceptionMessage is null)
        {
            return jobEvent;
        }

        return jobEvent with
        {
            ExceptionMessage = Regex.Replace(
                jobEvent.ExceptionMessage,
                @"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
                "[REDACTED-EMAIL]"),
        };
    };
});

Ein paar Dinge, die man dazu wissen sollte:

Was ist mit einem reinen Hangfire-Filter?

Technisch ist es möglich, einen eigenen IElectStateFilter zu schreiben, der FailedState.Exception direkt umschreibt, registriert mit einer niedrigeren Filter-Order als der eigene Filter von QueueHawk.Agent, damit er zuerst läuft. Der Unterschied: das schreibt um, was Hangfire selbst als Exception für diesen Job betrachtet — inklusive dessen, was das eigene Team im lokalen Hangfire-Dashboard und in den eigenen Applikationslogs sieht, nicht nur, was bei QueueHawk ankommt. OnBeforeSend zensiert nur die Kopie, die QueueHawk sieht, und lässt die eigene lokale Diagnose unangetastet. Für die meisten Teams ist das der nützlichere Default — die eigenen Entwickler sollen die echte Exception sehen, solange sie frisch ist, und nur das, was den Prozess verlässt, soll von sensiblen Details befreit werden.

Payloads bleiben der grössere Hebel

Wenn die Exception-Messages eines Jobs regelmässig Kundendaten referenzieren, ist das oft ein Zeichen, dass das zugrunde liegende Job-Design mehr preisgibt als nötig — ein Blick lohnt sich unabhängig vom eingesetzten Monitoring-Tool. Aber für die Fälle, in denen das unvermeidbar ist oder von einer Abhängigkeit stammt, die man nicht kontrolliert, ist Zensur an der Agent-Grenze der praktische Fix. Zusammen mit standardmässig deaktiviertem IncludeJobPayloads decken die beiden Optionen beide Stellen ab, an denen sensible Daten in einem Job-Event landen können: die Argumente und der Fehlertext.

Vollständige Options-Referenz inklusive aller Defaults: Agent-Konfigurationsreferenz.

← Alle Artikel Zur vollständigen Konfigurationsreferenz →

Monitoring, das respektiert, was es nicht sehen soll

Kostenlos für eine Application, keine Kreditkarte nötig.

Kostenlos starten