Přeskočit na obsah

Ukončená impersonace + stale formulář přepíše cizí profil

import { Aside } from ‘@astrojs/starlight/components’;

Účet administrátora se opakovaně „sám” přejmenoval na jméno + adresu + bankovní účet různých uživatelů (vždy toho, kterého admin před chvílí spravoval). Žádný útok — interní data-integrity bug. Záludné: i nejstarší zálohy už obsahovaly přepsaná data, takže původní jméno účtu nešlo obnovit.

Admin používá impersonaci (přepne session cookies na cílového uživatele, vlastní tokeny si schová). Endpoint pro úpravu vlastního profilu ukládá slepě podle session z cookie:

const { data: { user } } = await supabase.auth.getUser(); // ← identita z cookie
await supabase.from('profiles').update(updates).eq('id', user.id);

Sekvence vedoucí ke korupci:

  1. Admin impersonuje uživatele → otevře jeho profil → formulář se naplní jeho daty.
  2. Uloží (správně, session = uživatel).
  3. Ukončí impersonaci → cookies zpět na admina, redirect pryč.
  4. Tlačítkem Zpět / přes bfcache se vrátí na stále vyplněný formulář (cizí data v DOM), znovu uloží.
  5. Teď je session = admin → eq('id', user.id) zapíše cizí data na adminův účet.

Stejný princip funguje i bez bfcache kdekoli, kde se session přepne mezi načtením formuláře a jeho odesláním.

Formulář si při načtení zapamatuje, pro které ID byl naplněn, a pošle ho při uložení. Server zápis odmítne, když se identita mezitím změnila:

// klient (load): zapamatuj si vlastníka formuláře
window.__profileLoadedFor = user.id;
// klient (save): pošli to s sebou
body.expected_user_id = window.__profileLoadedFor;
// server (před update):
if (body.expected_user_id && body.expected_user_id !== user.id) {
return json({ error: 'Relace se změnila, profil nebyl uložen.' }, 409);
}

Admin-only editace uživatelů (přes explicitní user_id z těla requestu) byla bezpečná — zranitelný byl jen self-service endpoint, který identitu bral z cookie.

Jak se tomu vyvarovat v jiných systémech

Sekce “Jak se tomu vyvarovat v jiných systémech”
  • Detection: najdi self-service mutace tvaru update(...).eq('id', session.user.id), kde id pochází jen ze session. Zkombinuj s existencí impersonace / „login as user”.
  • Anti-pattern: formulář načtený v jednom identity-kontextu, odeslaný v jiném; spoléhání čistě na cookie session u zápisu, který nese předvyplněná data.
  • Lepší přístup: každý mutate request, který nese předvyplněná data, ať obsahuje očekávané ID vlastníka a server ho ověří proti session (optimistic-concurrency styl). Po ukončení impersonace navíc tvrdě reloadni a invaliduj bfcache (Cache-Control: no-store na chráněných stránkách).

Sister bugs / související

Sekce “Sister bugs / související”

Příbuzné pasti impersonace: obnova session po impersonaci banovaného uživatele (getUser selže), úniky identity mezi tabem s impersonací a běžným tabem (cookies jsou sdílené per-doména).

Přidal aiarchitekt.cz · 18. 6. 2026 2:00
Provozuje aiarchitekt.cz