Podpora SameSite cookie v Nette a CSRF

před rokem

David Grudl
Nette Core | 6864
+
+26
-

Doplnil jsem do Nette (konkrétně do nette/http) podporu pro SameSite cookie, což je první systémová obrana proti CSRF. CSRF útok spočívá v tom, že útočník vyláká uživatele webu A na svou stránku na webu B, která nepozorovaně provádí v prohlížeči uživatele a tedy pod jeho identitou nějakou skrytou nepříjemnou činnost na webu A.

Popis najdete třeba zde:

SameSite cookie mají za cíl nahradit $form->addProtection() nebo Nextras Secured Links a nevyžadují nastartování session.

Ačkoliv se podpora pro SameSite cookie objevila v prohlížečích teprve nedávno, podle Caniuse ji už najdete v 68 % zařízení, což ještě stoupne za měsíc po vydání iOS 12.

(Zároveň je potřeba dodat, že značné procento zařízení je nepodporuje, takže addProtection & spol. zatím opustit nelze.)

Jak vypadá podpora v Nette 2.4 (2018–09–18)?

Tak jednak přibyl nový parametr $sameSite v metodě Nette\Http\Response::setCookie(), který může nabývat hodnot 'Lax', 'Strict' nebo null. Ale to není tak zajímavé.

Daleko užitečnější ovšem je posílání session ID s nastavením sameSite: Lax. Lax říká, že pokud se na web odešle POST formulář z jiné domény, nestane se tak pod aktuálně přihlášeným uživatelem. Jednoduše v HTTP požadavku bude chybět cookie se session ID (týká se to i AJAXových požadavků, iframe apod).

Tuto ochranu spustíte konfigurací:

session:
    cookieSamesite: Lax

Pojďme k dál.

Změny na webu by správně měly dělat jen POST požadavky, ale zcela běžně se k tomu používají i GET požadavky, tedy odkazy, jako například <a n:href="...">smazat</a>. U těchto odkazů také nechceme, aby je bylo možné vyvolat útočníkem z jiné domény. Řešením je buď odkazy zaměnit za tlačítka, u kterých ochranu zajišťuje už výše uvedená ochrana cookie se session ID, nebo zůstat u odkazů a kontrolovat na straně presenteru, zda akce byly vyvolány ze stejné domény.

Ukázka nahrazení odkazu za POSTové tlačítko:

odkaz
<a n:href="...">smazat</a>

nahradíme za tlačítko (pozn. attribut form nefunguje v IE 11)
<button formaction="..." form=postform>smazat</button>

+ na začátek stránky přidáme
<form id=postform method=post></form>

Pokud nepoužijeme tlačítko a zůstaneme u odkazů, je třeba kontrolovat na straně presenteru, zda akci vyvolal náš server.

Nejprve tedy aktivujeme podporu pro tuto feature pomocí konfigurace:

http:
    sameSiteProtection: yes

# zároveň aktivuje i výše uvedené cookieSamesite: Lax, které už není potřeba nastavovat

A pro test na straně presenteru poslouží metoda Nette\Http\Request::isSameSite(). Pokud odkaz nebyl odkliknut na našem serveru, musíme uživatele přesměrovat jinam, protože opakovaný požadavek vyvolaný stisknutím tlačítka refresh by už nemohl být tímto způsobem chráněn.

class MyPresenter extends Presenter
{
    public function handleDelete(int $id)
    {
        if (!$this->getHttpRequest()->isSameSite()) {
            $this->redirect('Sign:out');
        }
    }
}

Pokud na web přijdeme s prohlížečem, který SameSite cookie nepodporuje, vše bude fungovat, jen prostě odkazy nebudou chráněné. Proto není třeba váhat s nasazením této doplňující ochrany.

Jak by mohla vypadat podpora v Nette 3?

V Nette 3 bude ochrana automaticky aktivní a nebude nutné ji aktivovat v konfiguraci.

Kontrola na straně presenteru by se zřejmě zjednodušila na uvedení anotace:

class MyPresenter extends Presenter
{
    /**
     * @secured (nebo @samesite ?)
     */
    public function handleDelete(int $id)
    {
    }
}

před rokem

suwer
Člen | 24
+
-4
-

Mne se to nelibi. Ne kvuli myslence, ale kvuli tomu, ze je to celkem slozite na pochopeni, pro pouziti to vyzaduje hodne „dalsich informaci“ a nakonec, neni to univerzalni.

před rokem

David Grudl
Nette Core | 6864
+
+18
-

Bezpečnostní věci jsou vždycky složité na pochopení, s tím se nedá moc dělat.

před 11 měsíci

Lukes
Člen | 58
+
+4
-

David Grudl napsal(a):
Pokud nepoužijeme tlačítko a zůstaneme u odkazů, musíme kontrolovat na straně presenteru, zda je vyvolal náš server. To by se v Nette 3 zřejmě zjednodušilo na uvedení anotace:

class MyPresenter extends Presenter
{
    /**
     * @secured (nebo @samesite ?)
     */
    public function handleDelete(int $id)
    {
    }
}

Nebylo by lepší se k tomu chovat jako k escapování, což znamená, že pokud nic neuvedu, tak je to ve výchozím nastavení nastavené a anotací to deaktivovat? Je tu nějaký důvod(bezpečnostní, výkonový…) proč by to tak nemělo být? Je to sice BC break, ale to by u Nette 3 nemuselo vadit. Přece jen víš jestli je použitý GET nebo POST.

Editoval Lukes (4. 9. 2018 10:32)

před 11 měsíci

David Grudl
Nette Core | 6864
+
+5
-

Myslíš v případě signálů, tedy metod handleXyz()? To není špatný nápad, asi to zkusím nasadit a uvidíme, jak se to osvědčí.

před 11 měsíci

Lukes
Člen | 58
+
0
-

Přesně tak. Ono většinou actionXyz() se nepoužívá na nějakou přímou akci, která něco mění v DB. Jediný případ, který mě napadá je nějaká API, ale zase do té se neautentizuje přes session id v cookies…

před 11 měsíci

David Grudl
Nette Core | 6864
+
0
-

Ještě je otázka, jak se v případě neoprávněného přístupu zachovat. Asi bude nutné někam přesměrovat, zobrazit jen chybovou stránku nestačí, protože uživatel by mohl stisknout F5 a tím požadavek zopakovat, ale tentokrát už by byl vyvolaný ze same site.

před 11 měsíci

Lukes
Člen | 58
+
0
-

Tak proč na tohle nevyužít ErrorPresenter. Samozřejmě by se muselo redirectovat místo forwardu. Myslím, že nic univerzálnějšího není. Ještě by šlo přesměrovat na action, na které byl signál volán. Nebo homepage?

Editoval Lukes (4. 9. 2018 12:52)

před 11 měsíci

ali
Člen | 309
+
0
-

David Grudl napsal(a):

Ještě je otázka, jak se v případě neoprávněného přístupu zachovat. Asi bude nutné někam přesměrovat, zobrazit jen chybovou stránku nestačí, protože uživatel by mohl stisknout F5 a tím požadavek zopakovat, ale tentokrát už by byl vyvolaný ze same site.

Co takhle nastavit destinaci v configu?

Pripade pokud by si nekdo nejaky konkretni handle chtel prizpusobit, tak by se to mohlo nastavit pres anotaci.

Editoval ali (4. 9. 2018 16:50)

před 10 měsíci

Lumeriol
Generous Backer | 14
+
+1
-

Napadá někoho, jak vyřešit problém typu:

Eshop – objednávka – přesměrování na platební bránu na jiný web (přes presenter->redirect) – návrat z platební brány na původní stránku bez zrušení sessions?

Respektive je možné vypnout tuto kontrolu pouze pro část webu, či spíše mít z některých domén povolený přístup?

před 3 měsíci

ic
Člen | 424
+
+1
-

Asi dobré to nasadit co možná nejdříve… podle posledního oznámení Googlu ( https://thehackernews.com/…cookies.html ) totiž bude v Chromu i funkce pro odstranění sledovacích cookies. A to která cookie je a není „sledovací“ se bude rozhodovat právě podle nastavení SameSite . A cookies postaru bez jakéhokoliv nastavení se budou interpretovat jako cookie třetí strany a pokud bude uživatel paranoidní a často je mazat bude se z takového webu i často odhlašovat. Mít to nastavené už dopředu (Google to zatím jen ohlásil jako plán, nic konkrétního zatím v Chromu není) může zajistit, že se nepromažou i dlouhodobější cookies, které nastavuji už teď i když by ta funkce měla přijít až za X měsíců.