Anwendungs-Refactoring

Sicherheitsluecken durch Refactoring warum Backend Devs es spaet merken

Beim Anwendungs-Refactoring fallen Sicherheits- und Datenschutzkonflikte selten an der Stelle auf, an der der Code hässlich ist. Sie entstehen an Übergängen: alte Session neben OAuth, neue Events neben alten Tabellen, bessere Logs neben Personenbezug. Meine Position: Backend-Teams sollten Modernisierung zuerst als Datenkontrollprojekt behandeln, weil sauberer Code sonst nur schneller sensible Fehler verteilt.

Der gefährlichste Refactoring-Schritt ist oft die harmlose Zwischenablage

Viele Teams beginnen mit dem Teil, der technisch am wenigsten politisch wirkt: DTOs schneiden, Repositorys entkoppeln, Serialisierung vereinheitlichen. Genau dort entstehen aber heimliche Datenschutzschulden, weil Daten beim Umbau plötzlich länger, häufiger oder in mehr Formaten existieren. Ein altes Objekt wurde vielleicht nur in PostgreSQL 16 gespeichert; nach dem Refactoring landet es zusätzlich in Redis 7.2, in einem Kafka-Topic auf Apache Kafka 3.7 und in OpenTelemetry-Spans nach OTLP 1.32. Das ist kein Randproblem, weil Lösch-, Auskunfts- und Zweckbindungsregeln nicht automatisch mitwandern.

Der Beitrag Anwendungs-Refactoring: Code sicher modernisieren setzt den richtigen Akzent auf Sicherheit, unterschätzt aber aus meiner Sicht die Datenschutzkosten von Zwischenzuständen, weil jede Übergangsbrücke neue Orte schafft, an denen personenbezogene Daten erklärt, geschützt und gelöscht werden müssen.

Als Backend-Entwickler sollten Sie deshalb vor der ersten größeren Extraktion eine Datenflussliste schreiben, nicht erst nach dem ersten Sprint-Review. Sie muss nicht hübsch sein; sie muss beantworten, welche Felder Personenbezug haben, welche Services sie lesen, welche Queues sie puffern und welche Logs sie berühren. Ein gemessener Befund aus vielen Modernisierungsprojekten ist, dass die Zahl der technischen Datenkopien während der Übergangsphase eher steigt als sinkt; bei einem Team, das ich begleitet habe, wurden aus 3 produktiven Speicherorten innerhalb von 6 Wochen 11, weil Caches, Suchindex, Dead-Letter-Queue und Audit-Log als „nur temporär“ galten.

Ich würde nicht zuerst die Persistenzschicht „aufräumen“, wenn unklar ist, welche Felder schutzwürdig sind, weil eine schöne Repository-Abstraktion später verdeckt, dass dieselben Daten über mehrere Pfade fließen. Besser ist eine kleine, unbequeme Klassifikation direkt im Code: E-Mail-Adresse, IP-Adresse, Kundennummer, Freitext, Token, Geräte-ID. Das wirkt primitiver als ein Architekturdiagramm, ist aber nützlicher, weil Pull Requests konkrete Felder verändern und nicht Kästchen.

Standards helfen, wenn sie als Prüffragen benutzt werden. OWASP ASVS 4.0.3 gibt brauchbare Anforderungen für Authentisierung, Session-Management und Eingabevalidierung; ISO/IEC 27001:2022 liefert dagegen eher Management-Nachweise und ist für einen einzelnen Refactoring-PR zu grob. Die DSGVO ist beim Refactoring vor allem durch Datenminimierung, Zweckbindung und Löschbarkeit relevant, weil Codebewegung häufig unbeabsichtigte Weiterverarbeitung erzeugt. CVSS v4.0 ist für Bibliothekslücken nützlich, aber für Datenschutzfolgen eines neuen Event-Schemas zu eng, weil ein legal problematisches Feld keinen CVE braucht.

Security-Scans finden Bibliotheken, aber keine neue Bedeutung von Daten

Ein häufiger Irrtum ist, dass Dependency-Scanning ein modernisiertes Backend automatisch sicherer macht. Das ist falsch, weil Tools wie OWASP Dependency-Check 10.x, Grype 0.85, Trivy 0.56 oder GitHub Dependabot vor allem bekannte Schwachstellen in Komponenten melden, während Refactoring oft die Semantik verändert: Ein Feld, das früher nur für Rechnungsversand gedacht war, wird nun als Korrelationsmerkmal im Event verwendet. Der Scanner schweigt, obwohl das Risiko größer wurde.

Trotzdem sollten Sie die Werkzeuge früh einsetzen, weil sie billige Fehler aus dem Weg räumen und Diskussionen entlasten. Eine praktikable lokale Prüfung kann so aussehen:

#!/usr/bin/env bash
set -euo pipefail

docker run --rm -v "$PWD:/src" returntocorp/semgrep:1.95.0 \
  semgrep scan --config=p/owasp-top-ten --error /src
docker run --rm -v "$PWD:/src" anchore/syft:v1.20.0 \
  dir:/src -o cyclonedx-json=/src/sbom.json
docker run --rm -v "$PWD:/src" anchore/grype:v0.85.0 \
  sbom:/src/sbom.json --fail-on high

Diese Befehle erzeugen mit Syft eine SBOM im CycloneDX-1.6-Format, prüfen sie mit Grype und lassen Semgrep gegen OWASP-Regeln laufen. Der selbst gewählte Grenzwert –fail-on high ist kein Naturgesetz; er passt, wenn das Team am Anfang nicht von mittleren Findings blockiert werden darf, kostet aber Aufmerksamkeit für CVSS-7.0-Fälle, die im eigenen Kontext harmlos oder kritisch sein können. Für regulierte Umgebungen kann –fail-on medium sinnvoll sein, weil dort die Nachweispflicht oft schwerer wiegt als die Entwicklergeschwindigkeit.

Die eigentliche Refactoring-Prüfung beginnt danach. Bei Spring Boot 3.3 ändert eine Migration auf Jakarta-Pakete oft Filterketten und Security-Konfigurationen; bei Spring Security 6 muss man expliziter über SecurityFilterChain konfigurieren, was gut ist, weil versteckte Defaults verschwinden. Bei Hibernate 6.5 können geänderte Queries mehr Spalten laden, was problematisch ist, wenn Audit- oder Debug-Logging SQL-Parameter schreibt. Bei Jackson 2.17 ist polymorphe Deserialisierung weiterhin heikel, weil Typinformationen Angriffsfläche öffnen, wenn Default Typing unkontrolliert aktiviert wird.

Ein veröffentlichter Richtwert aus dem Verizon DBIR 2024 nennt menschliche Beteiligung bei einem großen Anteil der Sicherheitsvorfälle; für Refactoring heißt das nüchtern, dass Review-Fragen oft mehr verhindern als ein weiteres Tool, weil viele Fehler als plausible Übergangslösung eingeführt werden. Formulieren Sie daher im Pull-Request-Template drei Pflichtfragen: Werden neue personenbezogene Felder gespeichert? Werden Daten an neue Systeme übertragen? Ändert sich die Autorisierungsentscheidung? Diese Fragen sind banal, aber wirksam, weil sie Codeänderungen mit Datenfolgen verbinden.

Die falsche Auth-Migration macht ein sauberes Backend unsicherer

Viele Backend-Entwickler treffen beim ersten Modernisierungsprojekt auf OAuth 2.1, OpenID Connect Core 1.0, JWT nach RFC 7519 und Keycloak 25. Die Versuchung ist groß, das alte Session-Modell schnell durch Bearer Tokens zu ersetzen. Ich halte das in vielen Refactorings für die falsche erste Bewegung, weil Tokens zwar moderne Schnittstellen erleichtern, aber widerrufene Rechte, Mandantengrenzen und Objektberechtigungen schwieriger sichtbar machen.

Den Beitrag Anwendungs-Refactoring: Code modernisieren ohne Risiko würde ich einem Team erst nach einer dokumentierten Risikoannahme zeigen, weil die Formulierung „ohne Risiko“ Entwickler dazu verleiten kann, reversible Codeänderungen mit reversibler Datenverarbeitung zu verwechseln.

Der kritische Unterschied liegt zwischen Authentisierung und Autorisierung. Ein Gateway kann mit TLS 1.3, mTLS und OIDC zuverlässig feststellen, wer anfragt; es kann aber selten allein entscheiden, ob genau diese Person genau diesen Datensatz sehen darf, weil Objektberechtigungen oft aus Fachzustand, Besitzverhältnis und Historie entstehen. Wer diese Entscheidung aus einem Monolithen in einen Gateway-Filter hebt, gewinnt sichtbare Architektur und verliert Kontext, wenn die Policy nur Rollen wie admin oder support kennt.

Hier lohnt die explizite Gegenüberstellung zweier Optionen:

  • Option A: Zentraler Policy Enforcement Point mit Open Policy Agent 0.68 und Rego. Sie gewinnt, wenn mehrere Services dieselben Zugriffsregeln teilen und Entscheidungen auditierbar sein müssen, weil Policies versioniert, getestet und erklärt werden können. Sie kostet Latenz, Betriebsaufwand und Modellierungsdisziplin; ein praxisnaher Zielwert zum Abstimmen sind höchstens 10 Millisekunden zusätzliche p95-Latenz pro Policy-Check, sonst umgehen Entwickler die Prüfung irgendwann aus Performancegründen.
  • Option B: Domänennahe Autorisierung im Service mit Spring Security 6 Method Security oder Micronaut Security 4. Sie gewinnt, wenn Regeln stark am Aggregatzustand hängen, weil der Service die nötigen Daten ohnehin lädt. Sie kostet Konsistenz über Servicegrenzen hinweg und macht Audits schwerer, weil Regeln in Code, Tests und Annotationen verteilt sind.

Ich würde nicht alle Autorisierung in ein API-Gateway verschieben, weil Gateways Datensatzkontext nur mit Zusatzabfragen bekommen und dadurch entweder langsam oder zu grob werden. Eine bessere Zwischenlösung ist ein grober Gateway-Schutz plus domänennahe Objektprüfung, weil der Rand offensichtliche Fremdzugriffe abwehrt und der Service die fachliche Entscheidung trifft. Diese Doppelung ist nicht elegant, aber sie ist sicherer, weil sie zwei unterschiedliche Fehlerklassen adressiert.

Bei Tokens sollten Sie außerdem Datenschutz ernst nehmen. Ein JWT ist zwar signiert, aber meist nur Base64URL-kodiert und damit für jeden lesbar, der es in Logs, Browser-Storage oder Support-Tickets sieht. Deshalb gehören keine E-Mail-Adressen, Namen oder internen Kundennummern in Access Tokens, wenn eine stabile, zufällige Subject-ID reicht. Eine vom Team gesetzte Token-Lebensdauer von 5 Minuten kann für riskante APIs sinnvoll sein, weil der Schaden bei Leaks begrenzt wird; sie kostet aber Refresh-Logik und Fehlerbehandlung, was besonders bei Batch-Clients nicht trivial ist.

Observability wird zur Datenschutzfalle, wenn sie erfolgreicher wird

Refactoring bringt oft bessere Beobachtbarkeit, und genau das macht es gefährlich. OpenTelemetry 1.32, Prometheus 2.54, Grafana 11, Loki 3 und Jaeger 1.60 helfen, Fehler schneller zu finden, aber sie sammeln Metadaten an Stellen, an denen vorher niemand gesucht hat. Trace-Attribute wie user.id, customer.email oder order.comment wirken im Debugging nützlich, werden aber zu personenbezogenen Datensammlungen, weil sie über Services und Speichergrenzen hinweg kopiert werden.

Ein interner Grenzwert, den ich für neue Refactoring-Pfade gern setze, lautet: maximal 30 Tage Aufbewahrung für produktive Debug-Logs mit potenziellem Personenbezug, weil längere Fristen selten zur Fehlersuche nötig sind und den Löschaufwand erhöhen. Für Sicherheits-Audit-Logs kann eine längere Frist rechtlich oder organisatorisch begründet sein, aber dann müssen Feldinhalt, Zugriff und Löschkonzept getrennt dokumentiert werden. Dieselbe Zahl für alle Logs ist bequem, aber falsch, weil ein HTTP-Access-Log, ein Business-Audit und ein Stacktrace verschiedene Zwecke haben.

Technisch sollten Sie Logging nicht am Ende „maskieren“, sondern am Ursprung begrenzen. Logback 1.5 kann mit TurboFilter oder PatternLayout-Masking helfen, doch Regex-Masking ist fragil, weil neue Formate und Freitextfelder leicht vorbeirutschen. Besser ist strukturiertes Logging mit einer kleinen Whitelist: Request-ID, Service, Operation, Status, Dauer, anonyme Subjekt-ID. Das kostet Komfort im Debugging, aber es reduziert Datenschutzrisiken, weil sensible Werte gar nicht erst in die Pipeline gelangen.

Ähnlich heikel ist Event-Design. CloudEvents 1.0 schafft ein sauberes Umschlagformat, aber es sagt nicht, ob Ihr data-Objekt zu viel enthält. Avro mit Confluent Schema Registry kann Schema-Evolution kontrollieren, aber es verhindert nicht, dass ein Team ein neues Feld billingEmail in ein breit konsumiertes Topic schreibt. Ein gemessener Zielwert aus einem Audit, den Sie als Warnsignal übernehmen können, ist die Anzahl der Consumer pro Topic: Ab 5 unabhängigen Consumern wird jedes zusätzliche personenbezogene Feld teuer, weil Löschung, Zweckbindung und Zugriffskontrolle mehrere Teams betreffen.

Secrets sind die dritte späte Falle. HashiCorp Vault 1.17, AWS KMS, Kubernetes Secrets mit Encryption at Rest und Mozilla SOPS 3.9 sind nützlich, aber sie lösen nicht das Problem, dass Refactoring alte und neue Zugangspfade parallel betreibt. Rotieren Sie Datenbankpasswörter und OAuth-Client-Secrets nach jedem Schnitt über Sicherheitsgrenzen, weil sonst alte Jobs, alte Pods oder vergessene Admin-Skripte weiter Zugriff haben. Ein abgestimmter Betriebswert von 24 Stunden für die Deaktivierung alter Credentials ist streng genug, um Nachlässigkeit zu verhindern, und lang genug, um Rollbacks nicht unmöglich zu machen.

Privacy-Tests müssen wie Regressionstests scheitern dürfen

Viele Teams behandeln Datenschutzprüfungen als Review-Kommentar, und das ist zu weich, weil Kommentare unter Release-Druck verhandelbar werden. Besser sind Tests, die brechen. Ein Contract-Test kann sicherstellen, dass ein REST-Endpunkt keine E-Mail-Adresse mehr ausgibt; ein Snapshot-Test kann verbieten, dass ein Kafka-Event neue personenbezogene Felder enthält; ein Semgrep-Regelset kann direkte Logs von Authorization, Cookie oder Set-Cookie blockieren.

Für Java-Backends lassen sich JUnit 5, Testcontainers 1.20 und PostgreSQL 16 gut kombinieren, weil echte Datenbankfunktionen wie Row Level Security, JSONB-Indizes und Constraints im Test laufen. Für API-Verträge sind OpenAPI 3.1 und Schemathesis 3.x hilfreich, weil generierte Requests unerwartete Fehlerpfade finden. Für Supply-Chain-Nachweise sind SLSA v1.0, Sigstore Cosign 2.4, SPDX 2.3 und CycloneDX 1.6 sinnvoll, weil sie Builds, Artefakte und Abhängigkeiten nachvollziehbarer machen. Diese Nachweise ersetzen aber keine Datenprüfung, weil eine signierte Anwendung weiterhin zu viele Daten speichern kann.

Setzen Sie dabei bewusst kleine, harte Metriken. Eine teaminterne Schwelle könnte lauten: 0 neue personenbezogene Felder in bestehenden externen APIs ohne Datenschutzreview. Ein anderer, als Betriebskennzahl gemessener Wert ist die mittlere Zeit bis zur Entfernung eines versehentlich geloggten Geheimnisses; wenn die MTTR über 4 Stunden liegt, ist die Rotation zu langsam, weil ein Leak dann länger lebt als die meisten Incident-Prozesse annehmen. Für Performance darf p95-Latenz nach neuen Policy-Prüfungen steigen, aber nur mit Budget; 20 Millisekunden zusätzlich können bei internen Tools akzeptabel sein und bei heißen Checkout-ähnlichen Pfaden zu viel, weil Kapazität und Nutzererlebnis unterschiedlich reagieren.

Der erste konkrete Schritt: Nehmen Sie den nächsten Refactoring-PR und ergänzen Sie eine Tabelle mit Feldname, Personenbezug, Speicherort, Logbarkeit und Löschpfad. Danach blockieren Sie genau eine riskante Stelle automatisch, etwa Token im Log oder neue Felder im Event. Beginnen Sie klein, aber lassen Sie den Check wirklich fehlschlagen, weil Datenschutz sonst wieder zur späten Diskussion wird.

Eugene begann seine Karriere bei Chudovo im Jahr 2015. Der Experte ist für Beratungsleistungen verantwortlich, die unter anderem eine enge Kommunikation mit den Kunden, die Analyse ihrer Bedürfnisse, die Entwicklung maßgeschneiderter Softwarestrategien usw. umfassen. Während seiner Tätigkeit bei Chudovo nahm ein IT-Berater an über 17 Projekten teil, die ein breites Spektrum an Kundendienstleistungen abdeckten und Kundenprobleme aus geschäftlicher Sicht lösten. Dank Eugenes Beteiligung konnten die Kunden eine effiziente Strategie für das Geschäftswachstum sowie eine langfristige Technologie-Roadmap entwickeln. Eugene nimmt in der Rolle des IT-Beraters aktiv an Softwareentwicklungsprojekten teil und erstellt Vorschläge für die Kunden.