presseportale/dev/sicherheit/SICHERHEITSAUDIT-2026-10-07.md

31 KiB
Raw Permalink Blame History

Sicherheitsaudit – Login, Sitzungen und Berechtigungen

Datum: 07.10.2026

Geprüfter Repository-Stand: 2215826f374b

Arbeitsverzeichnis: /var/www/html

Auftrag: ausschließlich Sicherheitsprüfung und Dokumentation; keine Fehlerbehebung.

1. Ergebnis

Die Anwendung hat bereits wesentliche Schutzmechanismen. Dennoch wurden sechs konkrete Sicherheitsprobleme im Code festgestellt: fünf mit hoher und eines mit mittlerer Priorität. Besonders relevant sind der Zugriff von Editoren auf fremde Alt-Rechnungen, eine ungeschützte Kontakt-Schreibaktion, verzögerter Entzug von Firmenrechten, fortbestehende Sitzungen nach Passwortänderungen und die automatische Kontoverknüpfung beim Google-Login.

Die Befunde sind durch nachvollziehbare Codepfade belegt. Eine gezielte dynamische Ausnutzung der sechs Befunde wurde nicht durchgeführt. Nach Klarstellung der Dev-Container-Umgebung wurden die vorhandenen Sicherheitstests und Paketprüfungen direkt ausgeführt; Ergebnisse siehe Abschnitt 6. Zusätzlich wurden zwei npm-Sicherheitsmeldungen für drei betroffene Pakete gefunden. Der Bericht ist keine Bestätigung der Sicherheit des produktiven Gesamtsystems.

ID Priorität Befund Nachweisstatus
S-01 Hoch Editor kann fremde Legacy-Rechnungs-PDFs abrufen Codepfad belegt
S-02 Hoch Kontaktzuordnung lässt sich ohne companies:write ändern Codepfad belegt
S-03 Hoch Automatische Firmenrechte bleiben nach Kontaktentfernung bestehen Codepfad belegt
S-04 Hoch Passwortreset/-wechsel widerruft bestehende Browser-Sitzungen nicht Codepfad belegt
S-05 Hoch Google-Kontoverknüpfung vertraut der E-Mail ohne ausreichende Identitätsprüfung Fehlende Prüfungen belegt; Angriff erfordert passende Google-Identität
S-06 Mittel Sensible Kontokonfiguration teilweise ohne erneuten Identitätsnachweis Codepfad belegt; bestehende Sitzung erforderlich

„Hoch“ bezeichnet hier erheblichen unberechtigten Datenzugriff, Schreibzugriff oder fortbestehenden Kontozugriff unter den jeweils genannten Voraussetzungen. Die Einstufung ist keine berechnete CVSS-Bewertung. Es wurde kein voraussetzungsloser anonymer Vollzugriff auf das System nachgewiesen.

2. Umfang und Methode

Geprüft wurden insbesondere:

  • Passwortlogin über Livewire und Fortify; Registrierung und E-Mail-Verifizierung.
  • Passwortreset, Passwortwechsel, Magic Links, Google-OAuth und Zwei-Faktor-Authentifizierung.
  • Sitzungsaufbau, Logout, Remember-Cookies und eigener Sitzungswiderruf.
  • Admin-/Editor-/Kunden-Grenzen, Policies, Firmenmitgliedschaften und ausgewählte Livewire-Schreibaktionen.
  • Rechnungsdownloads, Benutzer-Impersonation und API-Token-Verwaltung.
  • API-Authentifizierung, Token-Berechtigungen, Eigentümerfilter und aktive Konten.
  • Stichproben zu privaten Medien, Uploadvalidierung, HTML-Bereinigung, CSRF, HTTP-Headern und Proxy-Vertrauen.
  • Vorhandene Tests und sicherheitsrelevante Konfiguration. Geheimnisse wurden nicht in diesen Bericht übernommen.

Die Prüfung kombinierte Quellcodeanalyse, Nachverfolgung von Aufrufern und Schutzschichten sowie Abgleich mit versionsbezogener Laravel-/Livewire-Dokumentation und offizieller Google-Dokumentation. Kommentare zu früheren Audits wurden nicht als Wirksamkeitsnachweis übernommen; entscheidend war der aktuelle Code.

Laut Laravel-Boost-Paketinventar: PHP 8.4, Laravel 12.69.3, Fortify 1.40.0, Livewire 4.4.7, Sanctum 4.3.3 und Socialite 5.31.0. Dies ist eine Bestandsaufnahme, keine Aussage zur CVE-Freiheit dieser Versionen.

Nicht durchgeführt wurden Änderungen an Anwendungscode, Konfiguration, Abhängigkeiten oder bestehenden Benutzer-/Berechtigungsdaten; ebenso keine aktiven Angriffe auf Produktivsysteme. Die Tests verwenden eigene SQLite-In-Memory-Daten. Neben diesem Bericht wurde auf ausdrücklichen Benutzerwunsch eine Projektnotiz unter .ai/rules/general.md zur direkten Befehlsausführung im Dev-Container angelegt. Bereits vorhandene unversionierte Dateien unter dev/2026-10/ wurden nicht verändert.

3. Sicherheitsbefunde

S-01 – Editor kann fremde Legacy-Rechnungen herunterladen

Priorität: Hoch. Betroffen ist die Vertraulichkeit von Rechnungs- und Kundendaten.

Belege:

  • routes/customer.php:21 lässt auch aktive, verifizierte Editoren in den Kundenbereich; :69 registriert admin/me/legacy-invoices/{legacyInvoice}/pdf.
  • app/Models/User.php:561 schließt die Rolle editor in canAccessAdmin() ein.
  • app/Policies/LegacyInvoicePolicy.php:22 erlaubt über canAccessAdmin() den Zugriff auf jede Alt-Rechnung, unabhängig von Eigentümer und billing:manage.
  • app/Http/Controllers/LegacyInvoicePdfController.php:15 verwendet diese Policy und liefert anschließend die gespeicherte beziehungsweise neu gerenderte PDF.
  • Der eigentliche Admin-Rechnungsbereich ist dagegen in routes/admin.php:95 an billing:manage gebunden. Die Editor-Rolle besitzt dieses Recht nach app/Authorization/Permission.php:98 nicht.

Voraussetzung und nachvollziehbarer Prüffall: Ein aktiver, verifizierter Editor ohne billing:manage ruft die Kunden-Downloadroute mit der ID einer fremden Legacy-Rechnung auf. Die Policy gestattet den Abruf. Eine ausgeblendete Rechnungsübersicht verhindert diesen direkten Zugriff nicht.

Empfehlung: Fremde Rechnungen nur mit billing:manage freigeben; eigenen Belegzugriff separat erhalten. app/Policies/InvoicePolicy.php enthält bereits die entsprechende Unterscheidung für neue Rechnungen. Beide URL-Wege müssen dieselbe fachliche Berechtigung erzwingen.

Erforderlicher Regressionstest: Editor ohne Rechnungsrecht erhält beim fremden Legacy-PDF 403; Eigentümer erhält den eigenen Beleg; berechtigter Billing-Administrator erhält den fremden Beleg. Die Kunden- und Admin-Downloadroute separat prüfen.

S-02 – Schreibaktion auf der Firmenanzeige umgeht companies:write

Priorität: Hoch. Betroffen sind Datenintegrität und mittelbar Firmenberechtigungen.

Belege:

  • routes/admin.php:71 macht die Firmenanzeige für Editoren erreichbar. Andere CRM-Schreibrouten werden erst ab :74 durch can:companies:write geschützt.
  • resources/views/livewire/admin/companies/show.blade.php:56 definiert die öffentliche Aktion attachExistingContact() ohne Gate-/Policy-Prüfung.
  • Die Aktion überschreibt in :82 die company_id des ausgewählten Kontakts.
  • Auch das Aktualisieren von selectedExistingContactId ruft die Aktion direkt auf (:23). Die Ziel-ID $id ist eine nicht mit #[Locked] gesperrte öffentliche Property (:13).

Voraussetzung und Prüffall: Ein Editor ohne companies:write öffnet eine Firmenanzeige und löst die Aktion mit einem existierenden Kontakt aus. Die serverseitige Prüfung beschränkt sich auf die Existenz von Firma und Kontakt; das fehlende Schreibrecht wird nicht geprüft.

Auswirkung: Kontakte lassen sich gegen die vorgesehene Rollenbegrenzung zwischen Firmen verschieben. Zusätzlich leitet app/Services/Auth/ContactAccessService.php:282 aus Kontakt-E-Mail und Firmenzuordnung automatische responsible-Mitgliedschaften ab. Ein bereits vorhandener Kontakt mit kontrolliertem Postfach kann deshalb nach der Verschiebung zur Grundlage zusätzlicher Firmenrechte werden. Diese zweite Wirkung setzt einen entsprechenden Kontakt und eine spätere Synchronisierung voraus.

Empfehlung: companies:write unmittelbar vor der Mutation erzwingen und den Zugriff auf die beteiligten Datensätze prüfen. Die serverseitige Ziel-ID sperren. Eine reine UI-Beschränkung oder das Sperren der ID allein behebt die fehlende Aktionsautorisierung nicht.

Erforderlicher Regressionstest: Direkter Aktionsaufruf und Property-Update als Editor werden abgewiesen; die bestehende Kontaktzuordnung bleibt unverändert. Berechtigte Administratoren dürfen die Aktion weiterhin ausführen.

S-03 – Entfernte Kontakte behalten automatische Firmenrechte

Priorität: Hoch. Betroffen sind Vertraulichkeit und Integrität von Firmendaten und Pressemitteilungen.

Belege:

  • app/Services/Auth/ContactAccessService.php:123 berechnet automatische Mitgliedschaften neu und entzieht in :155 veraltete automatische Rechte.
  • Reguläre Aufrufstellen sind der Login-Listener app/Listeners/SyncCompanyMembershipsOnLogin.php:28 und die Bestätigung der eigenen neuen E-Mail in app/Http/Controllers/Auth/ConfirmEmailChangeController.php:46.
  • Kontaktänderung und -löschung in resources/views/livewire/customer/press-kits/show.blade.php:263 beziehungsweise :277 führen diese Bereinigung nicht aus.
  • Die registrierten EmailDomainObserver und AdminPerformanceCacheObserver prüfen Domains beziehungsweise leeren Anzeige-Caches; sie widerrufen keine Mitgliedschaften.
  • app/Models/User.php:601 verwendet die weiterhin gespeicherten Firmenzuordnungen. app/Policies/CompanyPolicy.php:75 akzeptiert die bestehende responsible-Rolle weiter als Schreibberechtigung.

Voraussetzung und Prüffall: Ein Benutzer hat ausschließlich über seine Kontaktadresse automatisch Firmenzugriff erhalten und ist bereits angemeldet. Die Kontaktadresse wird entfernt oder geändert; es gibt keine zweite gültige Zuordnungsgrundlage. Mit derselben Sitzung wird anschließend erneut auf die Firma beziehungsweise deren Pressemitteilungen zugegriffen.

Auswirkung: Der automatische Berechtigungsdatensatz bleibt bestehen und erlaubt weiterhin Zugriff, bis eine erneute Synchronisierung erfolgt. Ein aktiver Benutzer kann seinen nächsten Login hinausschieben; das ist kein verlässlicher Zeitpunkt für einen Rechteentzug. Manuell vergebene Rechte sind davon zu unterscheiden und dürfen nicht pauschal gelöscht werden.

Empfehlung: Bei Änderungen/Löschungen von Kontakt- und Firmen-E-Mail-Adressen sowie Kontaktverschiebungen die betroffenen automatischen Mitgliedschaften serverseitig neu bewerten. Den Entzug unmittelbar wirksam machen oder die aktuelle Berechtigungsgrundlage bei Zugriff prüfen. Manuelle und API-basierte Mitgliedschaften gezielt erhalten.

Erforderlicher Regressionstest: Nach Entfernung der einzigen Zuordnungsgrundlage wird der nächste Zugriff aus derselben bestehenden Sitzung abgewiesen. Varianten für E-Mail-Wechsel und Firmenwechsel ergänzen; manuelle Mitgliedschaften müssen erhalten bleiben.

S-04 – Passwortreset und Passwortwechsel lassen bestehende Sitzungen bestehen

Priorität: Hoch. Betroffen ist die Wiederherstellung der Kontosicherheit nach kompromittierten Zugangsdaten oder Sitzungen.

Belege:

  • resources/views/livewire/auth/reset-password.blade.php:44 setzt das Passwort und rotiert den Remember-Token, widerruft aber keine bestehenden Browser-Sitzungen.
  • resources/views/livewire/customer/security.blade.php:62, resources/views/livewire/settings/password.blade.php:22 und app/Actions/Fortify/UpdateUserPassword.php:22 ändern das Passwort ohne allgemeinen Sitzungswiderruf.
  • app/Services/Auth/SessionRevocation.php:34 bietet bereits einen eigenen Widerrufsmechanismus. Der gefundene Anwendungscode ruft ihn nur im Google-Onboarding eines zuvor unverifizierten Kontos auf (app/Services/Auth/SocialAuthService.php:65).
  • In bootstrap/app.php und den geschützten Web-Routen ist kein allgemeines auth.session/AuthenticateSession eingerichtet. Auch ein entsprechender eigener PasswordReset-Listener wurde nicht gefunden. Der Eintrag in config/sanctum.php ersetzt diese Web-Middleware nicht.

Voraussetzung und Prüffall: Zwei Browser sind mit demselben Konto angemeldet. In einem Browser wird das Passwort zurückgesetzt oder geändert. Die bereits authentifizierte Sitzung des zweiten Browsers bleibt durch die untersuchten Schutzschichten gültig.

Auswirkung: Ein Angreifer mit bestehender Sitzung kann nach der Passwortwiederherstellung weiterarbeiten. Die Rotation des Remember-Tokens beim Reset verhindert nicht die Verwendung einer bereits aufgebauten Sitzung. API-Tokens und noch gültige Login-Links werden ebenfalls nicht zentral als Wiederherstellungsmaßnahme behandelt; deren gewünschter Widerruf muss ausdrücklich festgelegt werden.

Empfehlung: Passwortreset mit einem zentralen Widerruf älterer Sitzungen verbinden. Beim angemeldeten Passwortwechsel mindestens andere Sitzungen beenden oder eine ausdrückliche, wirksame Option anbieten. Alle Passwortwege vereinheitlichen. Für einen vollständigen Kontowiederherstellungsprozess zusätzlich Remember-Cookies, API-Tokens und offene Login-Links berücksichtigen. Laravel beschreibt den erforderlichen Zusammenhang von auth.session und logoutOtherDevices() in der Authentifizierungsdokumentation.

Erforderlicher Regressionstest: Zwei unabhängig aufgebaute Sitzungen verwenden; nach Reset darf die alte Sitzung keine geschützte Seite und keine Livewire-Aktion mehr ausführen. Alle drei Passwortwechselwege prüfen. Gewolltes Verhalten für die aktuelle Sitzung, Remember-Cookies und API-Tokens separat festhalten.

S-05 – Google-Login verknüpft Konten ausschließlich anhand der E-Mail

Priorität: Hoch. Eine Kontoverwechslung kann zum Zugriff auf ein bestehendes Konto und dessen Firmenrechte führen.

Belege:

  • app/Http/Controllers/Auth/GoogleController.php:35 übergibt lediglich Provider, Provider-ID, E-Mail und Name. email_verified und hd werden nicht geprüft.
  • app/Services/Auth/SocialAuthService.php:36 sucht das lokale Konto ausschließlich anhand der E-Mail.
  • Eine vorhandene oauth_provider_id wird in :42 überschrieben, statt als stabile Identitätsbindung geprüft zu werden.
  • Neue beziehungsweise zuvor unverifizierte Konten werden anschließend als verifiziert behandelt.
  • Der installierte Socialite-Google-Provider stellt email_verified in den Rohdaten bereit, erzwingt beim Mapping aber keine entsprechende Anwendungsentscheidung (vendor/laravel/socialite/src/Two/GoogleProvider.php, mapUserToObject()).

Angriffsvoraussetzung: Der Angreifer benötigt eine von Google gelieferte Identität mit einer E-Mail, die zum lokalen Zielkonto passt, obwohl ihm das zugehörige Postfach nicht mehr gehört oder die erforderliche Verifizierung fehlt. Das ist insbesondere bei externen, später neu vergebenen E-Mail-Adressen relevant. Ein frei erfundener Callback-Parameter reicht nicht aus; Socialite verwendet weiterhin den regulären OAuth-Ablauf.

Einordnung: Google warnt ausdrücklich, dass es für externe E-Mail-Adressen ohne Workspace-Zuordnung (hd) keine verlässliche Aussage über den aktuellen Postfachbesitz trifft. Auch email_verified=true kann dort auf einer früheren Prüfung beruhen. Die Anwendung muss für solche Verknüpfungen zusätzliche Nachweise verlangen. Siehe Google: Backend-Authentifizierung.

Auswirkung und Grenze: Ohne aktivierte 2FA kann eine passende fremde Google-Identität den ersten Loginfaktor ersetzen. Bei bereits bestätigter 2FA wird im geprüften Controller weiterhin die Challenge verlangt; ein pauschaler 2FA-Bypass wurde nicht gefunden.

Empfehlung: Bestehende Verknüpfungen anhand der stabilen Provider-Identität auflösen und abweichende IDs nicht still überschreiben. email_verified explizit validieren. Für die erstmalige Verknüpfung mit einem vorhandenen Konto und für nicht autoritative externe Adressen einen aktuellen Nachweis über das lokale Konto beziehungsweise Postfach verlangen. Den Provider-Link erst nach erfolgreicher Prüfung speichern.

Erforderlicher Regressionstest: Fehlendes/falsches email_verified, externe Adresse ohne hd, bereits anders verknüpfte Provider-ID und wiedervergebene E-Mail als Negativfälle abdecken. Die vorhandenen Google-Tests simulieren überwiegend nur ID/E-Mail/Name und belegen diese Grenzen nicht.

S-06 – Sensible Kontokonfiguration ohne frischen Identitätsnachweis

Priorität: Mittel. Voraussetzung ist eine bereits verwendbare Sitzung, etwa nach Session-Diebstahl oder an einem unbeaufsichtigten Gerät.

Belege und zwei konkrete Fälle:

  1. resources/views/livewire/customer/security.blade.php:138 beginnt die erstmalige 2FA-Einrichtung ohne erneute Passwort-/Postfachbestätigung. :150 bestätigt lediglich den gerade neu erzeugten Faktor. Die Route in routes/customer.php:76 verlangt keine frische Authentifizierung. Ein Sitzungsinhaber kann bei bisher fehlender 2FA deshalb seinen eigenen Authenticator registrieren und den echten Nutzer bei künftigen Logins aussperren.
  2. resources/views/livewire/customer/security.blade.php:206 lässt bei Konten ohne Passwort und ohne aktive 2FA assertSecurityProof() ohne weiteren Nachweis passieren. Der nachfolgende E-Mail-Wechsel bestätigt nur die neue Adresse. Ein Angreifer mit einer solchen Sitzung kann seine eigene neue Adresse bestätigen und daraus dauerhaften Kontozugriff gewinnen, ohne das bisherige Postfach zu kontrollieren.

Bei schon aktiver 2FA verlangt der Code für Deaktivierung und Recovery-Code-Neuerzeugung einen zweiten Faktor; diese vorhandene Schutzmaßnahme wird hier nicht als fehlend bewertet. Ebenso handelt es sich nicht um einen belegten CSRF-Angriff: Benötigt wird eine gültige Sitzung mit nutzbarem Anwendungskontext.

Empfehlung: Vor erstmaliger 2FA-Einrichtung und Änderung der Login-Adresse einen zeitnahen Nachweis verlangen. Bei passwortlosen Konten etwa einen separaten kurzlebigen Bestätigungslink an die bisherige Adresse oder einen ausdrücklich dafür vorgesehenen erneuten Google-Login. Ein Code des gerade neu eingerichteten Authenticator ersetzt diesen Nachweis nicht. Sicherheitsänderungen zusätzlich an die bisherige Adresse melden.

Erforderlicher Regressionstest: Eine ältere, vollständig angemeldete Sitzung ohne frischen Nachweis darf weder erstmals 2FA aktivieren noch die Login-Adresse eines passwortlosen Kontos ersetzen. Nach erfolgreicher erneuter Identitätsprüfung müssen legitime Änderungen möglich bleiben.

4. Zusätzliche Risiken und Sicherheitshärtung

Diese Punkte werden ausdrücklich nicht als weitere dynamisch bestätigte Sicherheitslücken gezählt.

Thema Beobachtung Verbesserung / noch erforderlicher Nachweis
Proxy-Vertrauen bootstrap/app.php vertraut allen Proxies einschließlich X-Forwarded-For und X-Forwarded-Host. Vertrauenswürdige Proxy-Netze begrenzen oder nachweisen, dass nur der Proxy das Backend erreicht und fremde Forwarded-Header zuverlässig bereinigt. Andernfalls sind IP-basierte Limits und IP-Protokolle manipulierbar. Die reale Produktions-Proxykonfiguration wurde nicht geprüft.
Login-Rate-Limit Livewire-Login und Fortify begrenzen nach Kombination aus E-Mail und IP. Zusätzliche IP-weite und geeignete kontobezogene Erkennung für Password-Spraying/verteilte Versuche erwägen, ohne einfach provozierbare Kontosperren zu erzeugen. Wirksamkeit am tatsächlichen Proxy prüfen.
2FA für privilegierte Rollen 2FA ist unterstützt, aber im untersuchten Admin-Gate nicht verpflichtend. Für Administratoren und besonders Super-Administratoren verpflichtende 2FA und einen geregelten Wiederherstellungsprozess vorsehen.
Passwortqualität Die geprüften Formulare verwenden Password::defaults(); eine zentrale Verschärfung wurde nicht gefunden. Längere Passphrasen, passende Mindestlänge und Prüfung bekannter kompromittierter Passwörter erwägen. Den Schutz nicht nur an Sonderzeichenregeln festmachen.
API-Token-Lebensdauer config/sanctum.php setzt expiration auf null; die Kunden-Token-Erzeugung setzt kein Ablaufdatum. Ablauf, Rotation und Widerruf bei Sicherheitsvorfällen festlegen. Für Token-Erzeugung einen frischen Identitätsnachweis erwägen. Keine Behauptung, dass jeder bereits gespeicherte Token unbegrenzt gültig ist.
Widerruf im Cache SessionRevocation speichert die Widerrufsmarke befristet im Cache. Verhalten bei Cache-Flush, Ausfall und Eviction prüfen; gegebenenfalls dauerhafte Authentifizierungsversion am Benutzer vorsehen. Ein Cacheverlust sollte entzogene Sitzungen nicht wieder freigeben.
HTTP-Header SecurityHeaders setzt mehrere sinnvolle Header, aber keine CSP oder HSTS. Eine zu Livewire/Alpine passende CSP zunächst beobachtend einführen; HSTS und HTTPS-Zwang am produktiven Einstieg prüfen. Mögliche zusätzliche Proxy-Header sind unbekannt.
Cookies und Debug Die lokale .env ist ausdrücklich local mit Debug; Secure-Cookie ist in config/session.php umgebungsabhängig. Produktivkonfiguration separat prüfen: Debug aus, Secure/HttpOnly/SameSite passend, enge Cookie-Domain, TLS und sichere Session-Speicherung. Lokale Debugwerte sind kein Beleg für eine Produktionslücke.
Sicherheitsereignisse Login-/Fehlversuchsprotokollierung ist vorhanden; ein vollständiger zentraler Widerrufsprozess wurde nicht gefunden. Meldungen zu E-Mail-, Passwort- und 2FA-Änderungen, nachvollziehbare Rechteänderungen sowie eine Funktion zum Beenden fremder Sitzungen vorsehen. Tokens und Kennwörter niemals protokollieren.

5. Vorhandene Schutzmaßnahmen

Folgende Maßnahmen sind im geprüften Code positiv erkennbar; Umfang und Grenzen der ausgeführten Tests stehen in Abschnitt 6:

  • Passwort-, Magic-Link- und Google-Login berücksichtigen bestätigte 2FA. Unbenutzte Fortify-2FA-Verwaltungsrouten werden gezielt gesperrt.
  • Magic Links enthalten zufällige Tokens, speichern deren Hash und prüfen Ablauf sowie Verwendung. Beim Einlösen schützt ein bedingtes Update gegen parallele Wiederverwendung.
  • Passwortreset-Anforderungen verwenden eine neutrale Rückmeldung und Limits pro E-Mail sowie IP.
  • Die reguläre E-Mail-Änderung verwendet pending_email und einen signierten Bestätigungslink; Übernahme und Firmenabgleich erfolgen erst bei Bestätigung. S-06 betrifft den davor fehlenden erneuten Nachweis für bestimmte Konten.
  • Admin- und Kunden-Gates berücksichtigen den Aktivstatus. Eigene Panel-Middleware wird für Livewire-Folgeanfragen erneut registriert.
  • Die API verlangt Sanctum-Authentifizierung und aktive Konten; geprüfte Controller/Requests verwenden Token-Abilities und Eigentümerfilter. API-Schlüssel aus URL-/Body-Parametern werden nicht als Login akzeptiert.
  • Die Token-Oberfläche beschränkt Abilities, bindet Widerruf an den angemeldeten Benutzer und blockiert Token-Verwaltung während Impersonation.
  • Impersonation enthält einen Rangschutz für Super-Administratoren und blockiert den Admin-Bereich während der Übernahme.
  • Private Pressemedien verlangen Berechtigung oder eine gültige signierte Vorschau-URL. HTML-Inhalte werden über einen Purifier bereinigt; geprüfte Bild-Uploads schließen SVG aus und begrenzen Dateitypen/Größe.
  • CSRF ist regulär aktiv. Die ausdrücklich ausgenommenen Abmeldewege verwenden signierte Links.
  • Logout invalidiert die Sitzung und erneuert das CSRF-Token. .env und auth.json sind nicht versioniert und werden durch .gitignore ausgeschlossen. Das ist keine vollständige Prüfung der Git-Historie auf Geheimnisse.

6. Verifikation und Einschränkungen

Tatsächlich durchgeführt

  • Statische Prüfung der genannten Routen, Komponenten, Services, Policies, Middleware und relevanter installierter Framework-Bestandteile.
  • Abgleich vorhandener Tests, insbesondere tests/Feature/Auth/, tests/Feature/SecurityHardeningTest.php sowie der Schutzlogik in tests/TestCase.php und phpunit.xml.
  • Prüfung des vorgesehenen Testdatenbankschutzes: SQLite :memory: und zusätzlicher Guard vor RefreshDatabase; kein Testlauf wurde gestartet, der Bestandsdaten verändert hat.
  • Laravel-Boost-Paketinventar und Dokumentationssuche; offizieller Google-Dokumentationsabgleich für S-05.
  • Kontrolle des Repository-Status vor Erstellung des Berichts.

Korrektur zur Ausführungsumgebung

Die erste Prüfung verwendete entsprechend der ursprünglichen Projektanweisung Sail. Die folgenden Aufrufe endeten mit Docker or Podman is not running.:

vendor/bin/sail artisan route:list --path=login -vv --no-interaction
vendor/bin/sail artisan test --compact tests/Feature/Auth tests/Feature/SecurityHardeningTest.php tests/Feature/ApiAccessSecurityAndLoggingTest.php
vendor/bin/sail composer audit --locked --no-interaction
vendor/bin/sail npm audit --omit=dev

Diese Meldung belegt nicht, dass Docker auf dem Host gestoppt ist. Die Arbeitsumgebung befindet sich bereits im Dev-Container; Befehle werden dort direkt ausgeführt. Die Korrektur wurde auf Benutzerwunsch dauerhaft in .ai/rules/general.md festgehalten. Tests und Paketprüfungen wurden anschließend ohne Sail erneut gestartet. Die Tests wurden für diesen reinen Audit weder erweitert noch verändert. Die bei den Befunden beschriebenen Regressionstests bleiben Empfehlungen für eine spätere Behebung.

Erfolgreiche Testausführung – Nachprüfung vom 07.10.2026

Insgesamt: 189 Tests bestanden, 920 Assertions, keine fehlgeschlagenen Tests in diesen beiden ausgewählten Läufen. Die gesamte Anwendungstestsuite wurde nicht ausgeführt.

Prüfumfang Ergebnis Dauer
Authentifizierung, SecurityHardening, API-Zugriff/-Logging 118 bestanden, 578 Assertions 14,45 s
Editor-Berechtigungsgrenzen, Impersonation, Kundenprofil/-Sicherheit, Settings, Firmenkontext und PM-Firmenscope 71 bestanden, 342 Assertions 28,95 s

Ausgeführte Befehle (jeweils Exit-Code 0):

php artisan test --compact tests/Feature/Auth tests/Feature/SecurityHardeningTest.php tests/Feature/ApiAccessSecurityAndLoggingTest.php
php artisan test --compact tests/Feature/Admin/EditorPermissionBoundaryTest.php tests/Feature/Admin/UserImpersonationTest.php tests/Feature/CustomerProfileSecurityTest.php tests/Feature/Settings tests/Feature/PressReleaseCompanyScopeTest.php tests/Feature/CustomerCompanyContextTest.php
php artisan route:list --path=login -vv --no-interaction

Die Routenabfrage bestätigt drei Login-bezogene Routen einschließlich Fortify-POST-Login, Livewire-Loginseite und Magic-Link-Einlösung mit den registrierten Middleware-Ketten.

Einordnung: Bestandene vorhandene Tests widerlegen die sechs Codebefunde nicht. Beispielsweise decken Tests für gesperrte Editor-Verwaltungsseiten nicht automatisch den fremden Legacy-PDF-Abruf über die Kundenroute oder das Umhängen eines Kontakts auf der Firmenanzeige ab. Die in Abschnitt 3 benannten spezifischen Angriffsszenarien wurden nicht als neue Tests implementiert. Die Codebefunde bleiben offen; der Nachtrag belegt die erfolgreiche Ausführung der vorhandenen Schutztests.

Paket-Sicherheitsaudits – Nachprüfung vom 07.10.2026

composer audit --locked --no-interaction wurde erfolgreich ausgeführt (Exit-Code 0): keine bekannten Security-Advisories für die geprüften gesperrten PHP-Abhängigkeiten gemeldet. Dies ist eine zeitpunktbezogene Advisory-Abfrage, keine Garantie für Fehlerfreiheit.

npm audit --omit=dev wurde ausgeführt und meldet 3 betroffene Pakete: 2 kritisch, 1 hoch (Exit-Code 1 wegen der Funde). Dahinter stehen zwei unterschiedliche Advisories; concurrently wird wegen seiner betroffenen Abhängigkeit zusätzlich gezählt:

Paket im geprüften Bestand Einstufung laut Audit Sicherheitsmeldung Gepatchte Version laut Advisory
shell-quote@1.9.0 Kritisch Kommandoinjektion in quote() bei bestimmten Kombinationen von Kommentar-Token und nicht vertrauenswürdigem String mit Zeilenumbruch; GHSA-pqg4-j6r4-53mv shell-quote 1.11.0
concurrently@9.2.4 Kritisch, transitiv Verwendet das betroffene shell-quote; dieselbe Meldung, keine dritte eigenständige Schwachstelle Abhängigkeitskette auf eine Version mit korrigiertem shell-quote aktualisieren
source-map-js@1.2.1 Hoch Blockieren der Event Loop durch präparierte Section-Offsets in indizierten Source Maps; GHSA-68fv-2mgg-jv7q source-map-js 1.2.2

Die installierten Versionen und Abhängigkeitswege wurden mit npm ls shell-quote concurrently source-map-js --all sowie package-lock.json abgeglichen. concurrently wird laut package.json für dev:all verwendet; source-map-js hängt an der CSS-/Build-Werkzeugkette. Diese Werkzeuge stehen teilweise unter dependencies und werden deshalb trotz --omit=dev geprüft. Eine Erreichbarkeit der gefährdeten Funktionen durch anonyme Web-Anfragen wurde nicht nachgewiesen. Die Paket-Schweregrade dürfen nicht automatisch als bestätigte kritische Loginlücke interpretiert werden.

Empfehlung: die betroffenen Abhängigkeitsketten gezielt aktualisieren, danach Build und Audit erneut prüfen. Entsprechend dem reinen Audit-Auftrag wurde weder npm audit fix noch eine andere Paketänderung ausgeführt.

Offen geblieben

  • Dynamische Bestätigung der sechs Befunde in einer isolierten Testumgebung.
  • Produktionswerte, tatsächliche Rollen-/Permission-Zuordnung, Altbestand und Zahl betroffener Konten/Rechnungen.
  • TLS, Reverse-Proxy-Header, Firewall, Datenbank-/Redis-Erreichbarkeit, Betriebssystem und Backups.
  • Vollständige Prüfung der tatsächlichen produktiven Erreichbarkeit der npm-Funde; separat als devDependencies deklarierte npm-Pakete waren durch --omit=dev nicht Teil dieses Audits.
  • Vollständiger Penetrationstest aller Endpunkte und aller möglichen Race Conditions. Die Stichproben zu Uploads, HTML und API begründen keine pauschale Fehlerfreiheit dieser Bereiche.

7. Empfohlene Reihenfolge

  1. S-01 und S-02 schließen: konkrete Rollenüberschreitungen an Download und Schreibaktion.
  2. S-03 und S-04 schließen: Rechteentzug und Kontowiederherstellung müssen auch bei bereits bestehenden Sitzungen greifen.
  3. S-05 schließen: Google-Identität und Verknüpfung vorhandener Konten verbindlich prüfen.
  4. S-06 schließen: frische Identitätsnachweise vor dauerhaften Sicherheitsänderungen.
  5. Die gezielten Negativtests ergänzen, die gefundenen npm-Abhängigkeiten aktualisieren und die produktiven Proxy-/Cookie-/TLS-Einstellungen verifizieren.

Änderungsstatus: Audit-Dokumentation und ausdrücklich angeforderte Dev-Container-Projektnotiz erstellt/aktualisiert; keine Sicherheitskorrekturen umgesetzt.

8. Nachtrag – Umsetzung vom 07.10.2026

S-01 bis S-06 sind im Code behoben und durch Regressionstests abgesichert. Live seit v1.4.0 (07.10.2026), dazu der Hotfix v1.4.1 für den Abgleich der Firmenrechte zu S-03. Die Entscheidungen, Details, Testdateien, Deploy-Schritte und beide Vorfälle stehen in ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md, Abschnitt 0; das Deploy-Protokoll steht in dev/betrieb/git-deploy.md.

Ebenfalls in v1.4.0 aus Abschnitt 4 umgesetzt: die Pflicht für einen zweiten Faktor bei Super-Admins und Admins (Passkey oder Authenticator-App). Offen sind npm-Updates, Proxy, HSTS, Passwortregeln, eine dauerhafte Widerrufsmarke, der Ablauf von API-Tokens, das Login-Limit pro IP und CSP.