presseportale/app/Http/Controllers/Auth/ConfirmEmailChangeController.php
Kevin Adametz 1c07fcb84b security: Sicherheitsaudit 07.10.2026 (S-01–S-06) behoben, Passkeys und Zweitfaktor-Pflicht für Admins
Audit: dev/sicherheit/SICHERHEITSAUDIT-2026-10-07.md, Plan und Entscheidungen:
dev/sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md.

- S-01 Legacy-Rechnungen fremder Kunden nur mit billing:manage (auch Kundenroute).
- S-02 Kontaktzuordnung auf der Firmenanzeige verlangt companies:write, $id gesperrt.
- S-03 CompanyMembershipObserver: automatische Firmenrechte werden bei jeder
  Adress-/Zuordnungsänderung sofort neu bewertet; memberships:resync-auto.
- S-04 Passwortwechsel beendet alle anderen Sessions (PasswordChanger), Reset
  alle Sessions und offene Login-Links; Fortify PUT /user/password abgeschaltet.
  API-Tokens bleiben bestehen.
- S-05 Google: email_verified Pflicht, feste Bindung über die Provider-ID;
  Erstverknüpfung bestehender Konten außerhalb gmail/hd nur per Bestätigungslink
  in derselben Browser-Session.
- S-06 Frischer Identitätsnachweis (Passwort, App-Code, E-Mail-Code oder
  Passkey) vor E-Mail-Wechsel, erster 2FA-Einrichtung und Token-Erzeugung;
  Hinweis-Mails an die (bisherige) Kontoadresse.
- Entscheidung 5: Passkeys (Fortify, @laravel/passkeys) für alle; Super-Admins
  und Admins bestätigen die Session für den Admin-Bereich per Passkey oder
  App-Code (EnsureStrongAdminAuthentication, Notfallschalter
  AUTH_REQUIRE_STRONG_ADMIN); Reset des zweiten Faktors durch Super-Admins.

Neue Migration: create_passkeys_table. Suite 2.403 grün (1 übersprungen),
Larastan ohne Meldung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:09:35 +00:00

60 lines
2.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Auth;
use App\Enums\SecurityChange;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Services\Auth\ContactAccessService;
use App\Services\Auth\SecurityNotifier;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class ConfirmEmailChangeController extends Controller
{
/**
* Übernimmt eine ausstehende E-Mail-Änderung, nachdem der Kunde den
* signierten Link aus der Mail an die NEUE Adresse geklickt hat.
* Bis dahin bleibt die alte Adresse aktiv (kein Aussperren bei Tippfehlern).
*/
public function __invoke(Request $request, User $user, string $hash, ContactAccessService $contactAccess, SecurityNotifier $notifier): RedirectResponse
{
abort_unless($request->user()->is($user), 403);
if (! $user->pending_email) {
return redirect()->route('me.security')
->with('security-status', __('Es liegt keine ausstehende E-Mail-Änderung vor. Möglicherweise wurde der Link bereits verwendet.'));
}
abort_unless(hash_equals(sha1($user->pending_email), $hash), 403);
// Zwischenzeitlich vergeben? Dann Änderung verwerfen statt Konflikt anlegen.
if (User::query()->where('email', $user->pending_email)->whereKeyNot($user->id)->exists()) {
$user->forceFill(['pending_email' => null])->save();
return redirect()->route('me.security')
->with('security-error', __('Die neue E-Mail-Adresse wird inzwischen von einem anderen Konto verwendet. Die Änderung wurde verworfen.'));
}
$previousEmail = (string) $user->email;
$user->forceFill([
'email' => $user->pending_email,
'email_verified_at' => now(),
'pending_email' => null,
])->save();
// Die Firmenrechte hängen an der Adresse (Audit 7.1): automatische
// Rechte der alten Adresse sofort zurücknehmen, passende der neuen
// vergeben – nicht erst beim nächsten Login.
$contactAccess->syncMembershipsForEmail($user);
// Die bisherige Adresse erfährt von der Änderung (Sicherheitsaudit
// 07.10.2026, S-06) — sonst bliebe eine Übernahme per fremder Session
// für den Inhaber unsichtbar.
$notifier->notify($user, SecurityChange::EmailChanged, recipient: $previousEmail, newEmail: (string) $user->email);
return redirect()->route('me.security')
->with('security-status', __('Ihre E-Mail-Adresse wurde erfolgreich geändert.'));
}
}