Na navigaci | Klávesové zkratky

Co jsou SameSite cookie a proč je potřebujeme?

SameSite cookies poskytují mechanismus, jak rozpoznat, co vedlo k načtení stránky. Jestli to bylo prokliknutí odkazu na jiném webu, odeslání formuláře, načtení uvnitř iframe, pomocí JavaScriptu atd.

Rozlišit, jak byla stránka načtena, je totiž naprosto zásadní kvůli bezpečnosti. Závažná zranitelnost Cross-Site Request Forgery (CSRF) je tu s námi už dlouhých dvacet let a teprve SameSite cookie nabízí systémovou cestu, jak ji řešit.

Útok CSRF spočívá v tom, že útočník naláká oběť na stránku, která nenápadně v prohlížeči oběti vykoná požadavek na webovou aplikaci, na které je oběť přihlášena, a ta se domnívá, že požadavek vykonala oběť o své vůli. A tak pod identitou oběti provede nějaký úkon, aniž by ta o tom věděla. Může jít o změnu nebo smazání dat, odeslání zprávy atd. Aby aplikace útoku zabránila, musí rozlišit, jestli požadavek vznikl povolenou cestou, např. odesláním formuláře v ní samotné, nebo nějak jinak. SameSite cookie tohle umí.

Jak to funguje? Řekněme, že mám web běžící na nějaké doméně a vytvořím na něm tři různé cookies s atributy SameSite=Lax, SameSite=Strict a SameSite=None. Název ani hodnota nehrají roli. Prohlížeč si je uloží.

  1. Když libovolnou URL na mém webu otevřu přímým zadáním do adresního řádku nebo kliknutím na záložku, prohlížeč všechny tři cookie odešle.
  2. Když se na libovolnou URL na mém webu dostanu jakkoliv ze stránky z téhož webu, prohlížeč všechny tři cookie odešle.
  3. Když se na libovolnou URL na mém webu dostanu ze stránky z jiného webu, prohlížeč pošle jen cookie s atributem None a v určitých případech i Lax, viz tabulka:
Kód na jiném webu   Odeslané cookie
Link <a href="…"> None + Lax
Form GET <form method="GET" action="…"> None + Lax
Form POST <form method="POST" action="…"> None
iframe <iframe src="…"> None
AJAX $.get('…'), fetch('…') None
Image <img src="…"> None
Prefetch <link rel="prefetch" href="…"> None
  None

SameSite cookies dokáží rozlišit jen několik málo případů, ale jde právě o ty podstatné pro ochranu před CSRF.

Pokud mám třeba na webu v administraci formulář nebo nějaký odkaz pro smazání položky a ten byl odeslán/odkliknut, tak nepřítomnost cookie vytvořené s atributem Strict znamená, že se tak nestalo na mém webu, ale že požadavek přišel odjinud, tedy že jde o CSRF útok.

Cookie pro odhalení CSRF útoku vytvářejte jako tzv. session cookie bez atributu Expires, platnost je pak v podstatě nekonečná.

Doména vs site

„Na mém webu“ není to stejné jako „na mé doméně“, nejde o doménu, ale o web site (proto i název SameSite). Site sice často odpovídá doméně, ale třeba u služby github.io odpovídá subdoméně. Požadavek z doc.nette.org na files.nette.org je same-site, zatímco požadavek z nette.github.io na tracy.github.io je už cross-site. Tady je to hezky vysvětlené.

<iframe>

Z předchozích řádků již vyplynulo, že pokud je stránka z mého webu načtená uvnitř <iframe> na jiném webu, nepošle jí prohlížeč Strict ani Lax cookies. Je tu ale ještě jedna důležitá věc: pokud takto načtená stránka vytvoří Strict nebo Lax cookie, prohlížeč je ignoruje.

Tím vzniká možnost se bránit proti podvodnému získávání cookie neboli Cookie Stuffing, kde dosud systémová obrana taky chyběla. Trik spočívá v tom, že podvodník inkasuje provizi za affiliate marketing, ačkoliv uživatele na web obchodníka nepřivedl. Místo odkazu s affiliate ID, na který by musel uživatel kliknout, vloží do stránky neviditelný <iframe> se stejným odkazem a značkuje tak všechny návštěvníky.

Sušenky bez atributu SameSite se vždy posílaly při jakémkoliv same-site i cross-site požadavku. Stejně jako SameSite=None. Jenže v blízké budoucnosti začnou prohlížeče považovat příznak SameSite=Lax za výchozí, takže sušenky bez atributu budou považovány za Lax. Což je docela nebývale velký BC break v chování prohlížečů. Pokud chcete, aby se cookie i nadále chovala stejně a přenášela se při jakémkoliv cross-site požadavku, je potřeba jí nastavit SameSite=None. (Pokud nevyvíjíte embedované widgety apod., moc často to nechcete.) Bohužel pro loňské prohlížeče je hodnota None nečekaná. Safari 12 ji chápe jako Strict, takže na starších iOS a macOS vzniká ošemetný problém.

A ještě pozor: None funguje jen když je nastaven s atributem Secure.

Co udělat při útoku?

Utéct! Základní pravidlo sebeobrany, jak v reálném životě, tak na webu. Obrovskou chybou spousty frameworků je, že při detekci CSRF útoku zobrazí formulář znovu a napíší něco jako „Token CSRF je neplatný. Zkuste prosím formulář znovu odeslat“. Tím, že jej uživatel odešle znovu, je útok dokonán. Taková ochrana postrádá smysl, když vlastně uživatele vyzvete, aby ji obešel.

Ještě nedávno dělal Chrome v případě cross-site požadavku to, že po refreshi stránku zobrazil znovu, ale tentokrát cookie s atributem Strict poslal. Takže refresh vyřadil ochranu před CSRF založenou na SameSite cookie. Dnes už to naštěstí nedělá, ale je možné, že to dělají jiné nebo starší prohlížeče. Uživatel také může stránku „refreshnout“ kliknutím na adresní řádek + enter, což se bere jako přímé zadání URL (bod 1) a všechny cookie se odešlou.

Takže při detekci CSRF je nejlepší přesměrovat s HTTP kódem 302 jinam, třeba na homepage. Zbavíte se tak nebezpečných POST dat a ani problematická URL se neuloží do historie.

Nekompatibility

SameSite dlouho nefungovalo ani zdaleka tak, jak by mělo. Především kvůli chybám v prohlížečích a nedostatkům ve specifikaci, která třeba vůbec neřešila přesměrování nebo refresh. Samesite cookie se nepřenášely třeba při uložení nebo tisku stránky, naopak se přenášely po refreshi, když zrovna neměly atd. Naštěstí dnes už je situace lepší. Mám za to, že z vážných nedostatků přetrvává v aktuálních verzích prohlížečů jen ten výše zmíněný u Safari.

Doplnění: kromě SameSite lze velmi čerstvě rozlišit původ požadavku i hlavičkou Origin, což je nástupce hlavičky Referer více respektující soukromí uživatelů a pravopis.

Komentáře

  1. ic #1

    avatar

    A co Origin hlavička? To mi přijde jako snazší řešení CSRF zranitelnosti. Nebo je tam nějaký problém na který zapomínám?

    před 4 lety | reagoval [2] David Grudl
  2. David Grudl #2

    avatar

    #1 ici, mám za to, že cookies byl použitelné o něco dřív, takže jsem origin už pak nezkoumal.

    před 4 lety
  3. Elektricman #3

    avatar

    A čím je daný, že *.github.io je cross-site, ale *.nette.org je same-site?

    před 4 lety | reagoval [4] David Grudl
  4. David Grudl #4

    avatar
    před 4 lety
  5. Jakub Vrána #5

    avatar

    Při chybě přesměrovat na homepage považuji za velmi nešťastné. Cookie může zmizet kvůli expiraci, kvůli tomu, že uživatel uvolňoval místo na mobilu nebo z několika dalších důvodů. Lepší je zůstat na stránce a ujistit se, že uživatel chtěl opravdu danou operaci provést. Řekl bych, že šance nějaké chyby je mnohem větší než šance útoku.

    před 4 lety | reagoval [6] David Grudl
  6. David Grudl #6

    avatar

    #5 Jakube Vráno, Zmizet nemůže. Je naprosto nereálné, že si uživatel během vyplňování formuláře v administraci nebo těsně před odkliknutím odkazu smazat položku vymaže cookie na webu. A pokud to udělá, klidně jeho požadavek zahodím výměnou za ochranu všech uživatelů před CSRF.

    před 4 lety | reagoval [7] David Macek [8] David Grudl
  7. David Macek #7

    avatar

    #6 Davide Grudle, Pokud tokeny proti CSRF u vás nemají uměle omezenou platnost a vydrží pár dnů i paralelní práci ve více záložkách, pak bych byl ochoten možná souhlasit. Stává se mi ale, že mi legitimně vyplněný formulář neprojde, protože platnost jeho tokenu vypršela nebo byl token zneplatněn prací v druhé záložce. Pak by navržený přístup s explicitním zahozením dat byl ještě horší než současná loterie, kdy občas dostanu příležitost formulář znovu odeslat a občas jsem nucen ho navíc znovu vyplnit.

    před 4 lety
  8. David Grudl #8

    avatar

    #6 Davide Grudle, tento článek popisuje systémové řešení na bázi SameSite cookie. S „nekonečnou“ platností (session cookie), jedna cookie pro celý web. Všechny vámi zmiňované problémy tady vůbec neexistují.

    před 4 lety | reagoval [9] David Macek
  9. David Macek #9

    avatar

    #8 Davide Grudle, pravda. Děkuji za osvětlení.

    před 4 lety
  10. Patrik #10

    avatar

    Zhruba od 28. 7. 2020 se v chromě zobrazují problémy s nevyplněnu SameSite cookie. Nešlo si nevšimnout, že se to prakticky ukazuje všude kde je například reCaptcha od Googlu nebo Youtube atd. jelikož to využívá iframe. Ještě to moc nechápu, takže se asi zeptám blbě, ale zkusím to. Tyhle problémy se v consoli zobrazují, protože jsou na mé stránce a já si to musím opravit, nebo se vyskytují na stránkách Googlu (on si to musí opravit) a ke mě přecházejí kvůli tomu iframu?
    Varování se mi zobrazuje pouze u stránek, kde se tyto služby využívají a jejich cookie třetí strany. Koukal jsem, že stejný problém mají i jiné stránky, například https://www.vzhurudolu.cz/…t-proc-a-jak kde se youtube skytuji.

    Musí tohle opravit Google, nebo uživatel?

    před 4 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.


phpFashion © 2004, 2024 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.