security: Breadcrumbs ohne Daten an GlitchTip, Übergangsmarke der Session-Sperre entfernt
- ErrorReportScrubber entfernt die Daten aller Breadcrumbs und bereinigt ihre Nachrichten; Livewire-Breadcrumbs standardmäßig aus — das SDK hängt dort Formularwerte und Properties an (Roadmap 4.9). Bestand in GlitchTip geprüft: keine Formular- oder Passwortdaten. - SessionRevocation::legacyCachedMark() entfernt (Übergang bis v1.6.0) Changelog: Sicherheit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
29e68330ca
commit
d1b9ffdcf1
6 changed files with 49 additions and 18 deletions
|
|
@ -5,7 +5,6 @@ namespace App\Services\Auth;
|
|||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Illuminate\Contracts\Session\Session;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
|
|
@ -90,7 +89,7 @@ class SessionRevocation
|
|||
*/
|
||||
public function isRevoked(User $user, Session $session): bool
|
||||
{
|
||||
$revokedAt = max((int) $user->sessions_revoked_at_ms, $this->legacyCachedMark((int) $user->getKey()));
|
||||
$revokedAt = (int) $user->sessions_revoked_at_ms;
|
||||
|
||||
if ($revokedAt === 0) {
|
||||
return false;
|
||||
|
|
@ -98,14 +97,4 @@ class SessionRevocation
|
|||
|
||||
return (int) $session->get(self::SESSION_KEY, 0) < $revokedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Übergang: Bis v1.5.0 lag die Marke nur im Cache, mit Ablauf nach
|
||||
* `session.lifetime` + 5 Minuten. Sperren aus dieser Zeit sollen über den
|
||||
* Deploy hinweg gelten. Kann im Release nach v1.6.0 entfallen.
|
||||
*/
|
||||
private function legacyCachedMark(int $userId): int
|
||||
{
|
||||
return (int) Cache::get('auth.sessions-revoked-at.'.$userId, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Support\Monitoring;
|
|||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use Sentry\Breadcrumb;
|
||||
use Sentry\Event;
|
||||
use Sentry\EventHint;
|
||||
use Sentry\UserDataBag;
|
||||
|
|
@ -41,6 +42,8 @@ class ErrorReportScrubber
|
|||
$event->setUser($userId !== null ? UserDataBag::createFromUserIdentifier($userId) : null);
|
||||
$event->setRequest(self::scrubRequest($event->getRequest()));
|
||||
|
||||
$event->setBreadcrumb(array_map(self::scrubBreadcrumb(...), $event->getBreadcrumbs()));
|
||||
|
||||
foreach ($event->getExceptions() as $exception) {
|
||||
$exception->setValue(self::scrubText($exception->getValue()));
|
||||
}
|
||||
|
|
@ -58,6 +61,20 @@ class ErrorReportScrubber
|
|||
return $event;
|
||||
}
|
||||
|
||||
public static function scrubBreadcrumb(Breadcrumb $breadcrumb): Breadcrumb
|
||||
{
|
||||
$message = $breadcrumb->getMessage();
|
||||
|
||||
return new Breadcrumb(
|
||||
$breadcrumb->getLevel(),
|
||||
$breadcrumb->getType(),
|
||||
$breadcrumb->getCategory(),
|
||||
$message !== null ? self::scrubText($message) : null,
|
||||
[],
|
||||
$breadcrumb->getTimestamp(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $request
|
||||
* @return array<string, mixed>
|
||||
|
|
|
|||
|
|
@ -88,8 +88,10 @@ return [
|
|||
// Capture Laravel cache events (hits, writes etc.) as breadcrumbs
|
||||
'cache' => env('SENTRY_BREADCRUMBS_CACHE_ENABLED', false),
|
||||
|
||||
// Capture Livewire components like routes as breadcrumbs
|
||||
'livewire' => env('SENTRY_BREADCRUMBS_LIVEWIRE_ENABLED', true),
|
||||
// Capture Livewire components like routes as breadcrumbs — aus: hängt
|
||||
// Formularwerte und Properties an (Roadmap 4.9); der Scrubber leert
|
||||
// die Daten aller Breadcrumbs zusätzlich.
|
||||
'livewire' => env('SENTRY_BREADCRUMBS_LIVEWIRE_ENABLED', false),
|
||||
|
||||
// Capture SQL queries as breadcrumbs
|
||||
'sql_queries' => env('SENTRY_BREADCRUMBS_SQL_QUERIES_ENABLED', true),
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ Wenn der Auftrag lautet „schau in GlitchTip“, „was sagt GlitchTip“ o. Ä
|
|||
|
||||
## 5. Regeln, die nicht verletzt werden dürfen
|
||||
|
||||
- **Datenschutz (M4):** `send_default_pii` bleibt `false`, `max_request_body_size` bleibt `never`, Log- und Cache-Breadcrumbs bleiben aus, `ErrorReportScrubber` bleibt in `before_send`. Kein Name, keine E-Mail, keine IP an GlitchTip — nur die interne User-ID. Neue Query-Parameter, die mitgehen sollen, nur über `ErrorReportScrubber::AllowedQueryParameters` (und dieselbe Liste in `error-reporting.js` / Umami-Filter).
|
||||
- **Datenschutz (M4):** `send_default_pii` bleibt `false`, `max_request_body_size` bleibt `never`, Log- und Cache-Breadcrumbs bleiben aus, `ErrorReportScrubber` bleibt in `before_send` und entfernt die Daten aller Breadcrumbs; Livewire-Breadcrumbs bleiben aus (sie tragen Formularwerte und Properties, Roadmap 4.9). Kein Name, keine E-Mail, keine IP an GlitchTip — nur die interne User-ID. Neue Query-Parameter, die mitgehen sollen, nur über `ErrorReportScrubber::AllowedQueryParameters` (und dieselbe Liste in `error-reporting.js` / Umami-Filter).
|
||||
- **Keine variablen Zahlen in Fehlermeldungen**, wenn sie gruppiert werden sollen: GlitchTip gruppiert nach Text. `…after 20002 ms` und `…after 20001 ms` wurden drei getrennte Einträge (Issues 11, 13, 14). Variable Werte in den Kontext, nicht in die Meldung — oder einen Fingerprint setzen wie `ErrorSignal`.
|
||||
- **Vorübergehende Fremdfehler nicht einzeln melden.** Sonst flutet ein Google-Ausfall GlitchTip und verdeckt echte Fehler.
|
||||
- **Tests melden nie an GlitchTip** (Abschnitt 2). Neue Sentry-Tests nur mit `FakeGlitchTip`.
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ test('the revocation mark is stored on the account and survives a cache flush',
|
|||
$this->assertGuest();
|
||||
});
|
||||
|
||||
test('a revocation from before v1.6.0 that only lives in the cache still applies', function () {
|
||||
test('a cache-only mark from before v1.6.0 no longer revokes sessions', function () {
|
||||
/** @var TestCase $this */
|
||||
[$user, $otherSessionStamp] = accountWithOtherSession();
|
||||
|
||||
|
|
@ -199,6 +199,6 @@ test('a revocation from before v1.6.0 that only lives in the cache still applies
|
|||
$this->actingAs($user->fresh())
|
||||
->withSession([SessionRevocation::SESSION_KEY => $otherSessionStamp])
|
||||
->get(route('me.security'))
|
||||
->assertRedirect(route('login'));
|
||||
$this->assertGuest();
|
||||
->assertSuccessful();
|
||||
$this->assertAuthenticatedAs($user);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Illuminate\Contracts\Debug\ExceptionHandler;
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Features\SupportFileUploads\MissingFileUploadsTraitException;
|
||||
use Livewire\Features\SupportLockedProperties\CannotUpdateLockedPropertyException;
|
||||
use Sentry\Breadcrumb;
|
||||
use Sentry\Event;
|
||||
use Sentry\ExceptionDataBag;
|
||||
use Sentry\SentrySdk;
|
||||
|
|
@ -110,6 +111,28 @@ test('messages and exception values lose e-mail addresses', function () {
|
|||
->and($scrubbed?->getUser())->toBeNull();
|
||||
});
|
||||
|
||||
test('breadcrumbs lose their data, so form values never reach glitchtip', function () {
|
||||
$event = Event::createEvent();
|
||||
$event->setBreadcrumb([
|
||||
new Breadcrumb(Breadcrumb::LEVEL_INFO, Breadcrumb::TYPE_DEFAULT, 'livewire', 'Component booted: customer.security', [
|
||||
'updates' => ['password' => 'Geheim-12345!', 'email' => 'kunde@example.com'],
|
||||
]),
|
||||
new Breadcrumb(Breadcrumb::LEVEL_INFO, Breadcrumb::TYPE_DEFAULT, 'db.sql.query', 'select * from users where email = info@firma.de', ['executionTimeMs' => 1.2]),
|
||||
]);
|
||||
|
||||
$breadcrumbs = ErrorReportScrubber::beforeSend($event)?->getBreadcrumbs() ?? [];
|
||||
|
||||
expect($breadcrumbs)->toHaveCount(2)
|
||||
->and($breadcrumbs[0]->getMetadata())->toBe([])
|
||||
->and($breadcrumbs[0]->getMessage())->toBe('Component booted: customer.security')
|
||||
->and($breadcrumbs[1]->getMetadata())->toBe([])
|
||||
->and($breadcrumbs[1]->getMessage())->toBe('select * from users where email = [email]');
|
||||
});
|
||||
|
||||
test('livewire breadcrumbs are switched off by default', function () {
|
||||
expect(config('sentry.breadcrumbs.livewire'))->toBeFalse();
|
||||
});
|
||||
|
||||
test('the test suite never reports to the real glitchtip', function () {
|
||||
// Ohne diese Absicherung landeten absichtlich provozierte Fehler
|
||||
// („Queue weg“, „Datenbank nicht erreichbar“) als Issues in GlitchTip.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue