Prevence Cross-Site Request Forgery (CSRF)
- K útoku Cross-Site Request Forgery (CSRF) dochází, když škodlivý web, email, blog, zpráva, program, atp. způsobí, že prohlížeč provede nežádoucí akci na důvěryhodném webu, když je uživatel přihlášený
- Funguje proto, že požadavky prohlížeče zahrnují všechny cookies a sessions
- Pokud je uživatel přihlášený, není možné rozlišit, zda jde o akci uživatele nebo ne
- Dopad je omezen na možnosti aplikace a oprávnění uživatele, například
- převod finančních prostředků
- změna hesla
- provedení nákupu s přihlašovacími údaji uživatele
- Slouží útočníkovi k tomu, aby přiměl systém k provedení funkce prostřednictvím prohlížeče a účtu oběti
- Následujte tyto kroky k předcházení CSRF
- Zkontrolujte, zda má váš framework integrovanou CSRF ochranu a použijte ji
- Pokud ne, přidejte CSRF tokeny do všech požadavků měnících stav (způsobující akce) a ověřte je na backendu
- Použijte synchronizer token pattern pro stavový software
- Použijte cookies s dvojitým odesláním pro bezstavový software
- Implementujte alespoň jednu variantu
- Použijte
SameSite
cookie atribut pro sessions- Nenastavujte cookie speciálně pro doménu (subdomény sdílejí stejný soubor) - problém, když má subdoména CNAME na domény, které nemáte pod kontrolou
- Implementujte ochranu založenou na interakci s uživatelem u vysoce citlivých operací
- Použijte vlastní hlavičky u požadavků
- Ověřte origin pomocí standardních hlaviček
- Použijte
- Nezapomeňte, že jakýkoli XSS může sloužit k překonání všech zmírňujících mechanismů CSRF
- Předcházejte XSS zranitelnosti
- Nepoužívejte GET požadavky pro operace, které mění stav
- Chraňte tyto prostředky proti CSRF, pokud to děláte
Zmírnění na základě tokenů
- Synchronizer token pattern je jednou z nejoblíbenějších a nejdoporučovanějších metod pro zmírnění CSRF
Použijte vestavěnou nebo existující CSRF implementaci
- Zabudováno v mnoha frameworcích
- Prozkoumejte, zda váš framework tuto možnost podporuje před implementací vlastního řešení
- Nakonfigurujte CSRF nastavení frameworku důkladně
Synchronizer token pattern
- Generujte token na straně serveru
- Mohou být generovány
- jednou (během session uživatele)
- Uložte vygenerovanou hodnotu do session a používejte ji pro následující požadavek, dokud nevyprší
- pro každý požadavek (bezpečnější, ale přináší problémy s použitelností - ztížená možnost “Zpět”)
- jednou (během session uživatele)
- Ověřte existenci a platnost tokenu pro každý požadavek
- Odmítněte požadavek, pokud se neshoduje s hodnotou v session nebo chybí
- Zalogujte událost jako potenciální CSRF v případě selhání ověření
- CSRF token je
- jedinečný pro uživatelskou session
- tajný
- nepředvívatelný (bezpečně vygenerované velké náhodné číslo)
Nepřenášejte tokeny pomocí cookie (u tohoto patternu)
- Token může být na klienta předán jako HTML nebo JSON
- Na server může být poslán jako skryté pole, hlavička, JSON
- Zajistěte, aby se token nezapsal do logu nebo nebyl součástí URL
- CSRF token v GET požadavku může uniknout na několika místech
- Historie prohlížeče
- Logy
- Network utility
- Referer hlavička
<form action="/transfer.do" method="post">
<input type="hidden" name="CSRFToken" value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZMGYwMGEwOA==">
[...]
</form>
- Vložení tokenu do vlastní hlavičky prostřednictvím javascriptu je bezpečnější než přidání do skrytého pole formuláře (využívá vlastní hlavičky požadavku)
Dvojité odeslání cookie
- Pokud je udržování stavu tokenu na serveru problematické
- Bezstavové, snadná implementace
- Odesílání náhodné hodnoty v cookie i jako parametr požadavku
- Server ověřuje, zda se hodnota v cookie a hodnota v požadavku shodují
- V případě, že se hodnoty shodují, požadavek je přijat, v opačném případě je odmítnut
- Zahrňte token do šifrované cookie (jiné než ověřovací cookie) pro zvýšení bezpečnosti
- Subdoména nemá možnost přepsat zašifrovanou cookie bez potřebných informací (klíč)
- Jednodušší alternativou je HMAC (klíč zná pouze server)
Techniky obrany
SameSite
cookie atribut
- Jeho cílem je zmírnit CSRF útoky
- Pomáhá prohlížeči rozhodnout, zda má cookies posílat spolu s cross-site požadavky
**Strict**
- Zabrání odeslání cookie na cílovou stránku ve všech případech procházení mezi stránkami
- Např. GitHub - pokud přihlášený uživatel klikne na odkaz s firemním soukromým repozirářem, cookies se nepošlou a uživatel nebude mít přístup k tomuto projektu
- Např. banka nechce povolit žádné externí weby, odkud vedou odkazy na jejich transakční stránky
**Lax**
- Výchozí hodnota, rozumná rovnováha mezi bezpečností a použitelností
- Např. GitHub výše 👆 by v případě GET požadavku cookies odeslal, ale v případě POST by je zablokoval
Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict
Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax
- Atribut je podporován všemi druhy prohlížečů
- Implementujte atribut jako další vrstvu obrany
- Nenahrazuje použití CSRF tokenu, měl by s ním naopak koexistovat
- Existují 2 způsoby, jak toto zabezpečení obejít
Ověřování Origin pomocí hlaviček
- Určete původ požadavku (source origin) pomocí hlaviček
Origin
neboReferer
- Určete, kam požadavek směřuje (target origin)
- Na straně serveru ověřte, že se obě hodnoty shodují (source a target)
- Tyto hlavičky nelze programově měnit, proto jde o spolehlivé ověření (hlavičky může nastavit pouze prohlížeč)
Identifikace zdrojového originu
Kontrola Origin
hlavičky
- Ověřte, zda hodnota odpovídá cílovému originu, pokud je hlavička přítomna
Kontrola Referer
hlavičky
- Ověřte, zda název hostitele odpovídá cílovému originu, pokud hlavička
Origin
není přítomna - Způsob se běžně používá u požadavků před přihlášením (pro sledování synchronizačního tokenu)
- Ujistěte se v obou případech, že je kontrola originu cíle správná
- Ujistěte se, že
example.com.attacker.com
neprojde kontrolou, pokud je webexample.com
- Ujistěte se, že
- Zablokujte požadavek, pokud není přítomna ani jedna z výše uvedených hlaviček
- Zalogujte a sledujte všechny takové případy
- Požadavek můžete začít blokovat až poté, co budete mít dostatečnou jistotu ohledně používání a chování
Identifikace cílového originu
- Určení cílového originu není snadné
- Aplikační server je za jedním nebo více proxy servery a URL adresa se může lišit
- Pokud je server za proxy, existují tyto možnosti
- Nakonfigurujte aplikaci tak, aby znala svůj cílový origin
- Je to vaše aplikace = můžete zjistit cílový origin a nastavit ho v konfiguraci
- Nejbezpečnější přístup (je definován na serveru)
- Údržba může být problematická, pokud je aplikace nasazena na mnoha místech (dev, test, prod)
- Zabezpečte úložiště konfigurace, protože na něm závisí tato ochrana
- Použijte
Host
hlavičku- Účelem hlavičky je uvést cílový origin požadavku
- Pokud je server za proxy, hodnota se bude nejspíš lišit od hodnot v
Origin
aReferer
- Použijte
X-Forwarded-Host
hlavičku- Předejte problému s proxy serverem
- Obsahuje původní hodnotu hlavičky
Host
- Nakonfigurujte aplikaci tak, aby znala svůj cílový origin
- Ověření funguje správně, pokud jsou přítomny hlavičky
Origin
aReferer
, jsou však případy, kdy hlavičky nastavené nejsou- Ochrana soukromí
- Vyladění ekosystému prohlížečů
- Internet Explorer 11 nepřidává hlavičku
Origin
- Při 302 přesměrování není původ uveden (považováno za citlivou informaci)
- U požadavků se stejným originem je hlavičky
Origin
zahrnuta pouze u POST, DELETE a PUT požadavků - Load balancery, proxy a embedded network zařízení odstraňují
Referer
z důvodu ochrany osobních údajů
Cookie s __Host-
prefixem
- Použijte cookie prefixes s CSRF tokenem
- Pokud má cookie
__Host-
prefix (Set-Cookie: __Host-token=RANDOM; path=/; Secure
)- nelze ji přepsat / zapsat z jiné domény
- musí mít cestu
/
- musí být označena jako
Secure
(nesmí být odeslána přes nešifrovaný HTTP protokol)
- Je podporován všemi prohlížeči kromě Internet Exploreru
Použijte vlastní hlavičky pro požadavky
- Přidání CSRF tokenů, cookies, dvojitého odeslání, šifrovaného tokenu atp. často zahrnuje změnu uživatelského rozhraní a bývá problematické
- Použijte vlastní hlavičku jako alternativu pro endpointy (ajax, API)
- Opírá se o omezení zásady stejného původu (SOP - same-origin policy)
- K přidání vlastní hlavičky stačí použít javascript (pouze v rámci jeho původu)
- Prohlížeče ve výchozím nastavení neumožňují javascriptu provádět požadavky napříč originy s vlastními hlavičkami
- Ověřte přítomnost hlavičky a hodnotu na všech endpointech (na serveru)
- Má dvě výhody
- Nevyžaduje změny uživatelského rozhraní
- Nezavádí žádný stav na straně serveru (vhodné např. pro REST)
CSRF obrana na základě interakce uživatele
- Někdy je snazší a vhodnější zapojit uživatele k zabránění neoprávněných operací
- Silná obrana proti CSRF s dopadem na uživatelské prostředí
- Použijte ji pouze pro kritické operace (změna hesla, převody peněz) spolu s dalšími opatřeními
Login CSRF
- Tendence ignorovat CSRF na přihlašovacích formulářích
- Vývojáří předpokládají, že se na ně CSRF zranitelnost nevztahuje (uživatel ještě není přihlášen)
- Vytvořte předběžnou session (před přihlášením) a přidejte tokeny do přihlašovacího formuláře
- Zničte session po přihlášení a vytvořte novou, aby se zabránilo session fixation
CSRF na straně klienta
- Manipulací se vstupními parametry útočník obelstí javascript kód na straně klienta
- Vzniká, když javascriptový program používá pro generování asynchronních požadavků útočníkem kontrolované vstupy (URL adresa)
- Může obejít některá běžná CSRF opatření (tokeny, session cookies, SameSite)
- JS program je zahrne do asynchronních požadavků, čímž se zásady obejdou
- V případě klasického CSRF je zranitelnou komponentou server, zde je to klient
- Umožňuje útočníkovi generovat libovolné požadavky
- Server není schopen rozlišit, zda byl požadavek úmyslný nebo ne
Příklad
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
function ajaxLoad(){
// process the URL hash fragment
let hash_fragment = window.location.hash.slice(1);
// hash fragment should be of the format: /^(get|post);(.*)$/
// e.g., https://site.com/index/#post;/profile
if(hash_fragment.length > 0 && hash_fragment.indexOf(';') > 0 ){
let params = hash_fragment.match(/^(get|post);(.*)$/);
if(params && params.length){
let request_method = params[1];
let request_endpoint = params[3];
fetch(request_endpoint, {
method: request_method,
headers: {
'XSRF-TOKEN': csrf_token,
// [...]
},
// [...]
}).then(response => { /* [...] */ });
}
}
}
// trigger the async request on page load
window.onload = ajaxLoad();
- Program volá funkci
ajaxLoad()
, která je zodpovědná za načítání různých prvků na stránce - Funkce přečte URL hash a extrahuje z ní metodu a endpoint
- Vygeneruje asynchronní HTTP požadavek - zde nastává problém
- Oba vstupy (metoda a endpoint) mohou být ovládány útočníky
- Pro zneužití stačí s obětí sdílet škodlivou URL adresu (spear-phishing email)
- Více informací naleznete v článku
Zmírnění client-side CSRF
- Negenerujte asynchronní požadavky prostřednictvím vstupů, které může útočník ovládat (URL, název okna)
- Validujte vstup - formát a hodnota parametrů
- Předdefinujte data požadavku (endpointy, metody, parametry)
JavaScript guide
- Připojte CSRF token k metodám, které mění stav (POST, PUT)
Ukládání CSRF tokenu v DOM
- Lze zahrnout do
<meta>
tagu, javascript proměnné nebo kdekoli v DOM - Neukládejte jej do cookies nebo localStorage
<meta name="csrf-token" content="{{ csrf_token() }}">
Přepsání výchozích nastavení pro nastavení vlastních hlaviček
- Některé knihovny umožňují přepsat výchozí nastavení a automaticky přidávat hlavičku do všech ajax požadavků
XMLHttpRequest (nativní javascript)
- Přepište metodu
open()
tak, abynastavila hlavičkuanti-csrf-token
při každém volání této metody
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS)$/.test(method));
}
var o = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(){
var res = o.apply(this, arguments);
var err = new Error();
if (!csrfSafeMethod(arguments[0])) {
this.setRequestHeader('anti-csrf-token', csrf_token);
}
return res;
};
AngularJS
- Více informací naleznete v dokumentaci
$httpProvider
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
var app = angular.module("app", []);
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.post["anti-csrf-token"] = csrf_token;
$httpProvider.defaults.headers.put["anti-csrf-token"] = csrf_token;
$httpProvider.defaults.headers.patch["anti-csrf-token"] = csrf_token;
// AngularJS does not create an object for DELETE and TRACE methods by default, and has to be manually created.
$httpProvider.defaults.headers.delete = {
"Content-Type" : "application/json;charset=utf-8",
"anti-csrf-token" : csrf_token
};
$httpProvider.defaults.headers.trace = {
"Content-Type" : "application/json;charset=utf-8",
"anti-csrf-token" : csrf_token
};
}]);
Axios
- Axios umožňuje nastavit výchozí hlavičky pro POST, PUT, DELETE a PATCH
var csrf_token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
axios.defaults.headers.post['anti-csrf-token'] = csrf_token;
axios.defaults.headers.put['anti-csrf-token'] = csrf_token;
axios.defaults.headers.delete['anti-csrf-token'] = csrf_token;
axios.defaults.headers.patch['anti-csrf-token'] = csrf_token;
// Axios does not create an object for TRACE method by default, and has to be created manually.
axios.defaults.headers.trace = {}
axios.defaults.headers.trace['anti-csrf-token'] = csrf_token
JQuery
- Nabízí
$.ajaxSetup()
rozhraní pro přidání hlavičky
var csrf_token = $('meta[name="csrf-token"]').attr('content');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("anti-csrf-token", csrf_token);
}
}
});