Přeskočit na hlavní obsah

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
  1. 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
  2. Použijte synchronizer token pattern pro stavový software
  3. Použijte cookies s dvojitým odesláním pro bezstavový software
  4. Implementujte alespoň jednu variantu
    1. 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
    2. Implementujte ochranu založenou na interakci s uživatelem u vysoce citlivých operací
    3. Použijte vlastní hlavičky u požadavků
    4. Ověřte origin pomocí standardních hlaviček
  5. Nezapomeňte, že jakýkoli XSS může sloužit k překonání všech zmírňujících mechanismů CSRF
    1. Předcházejte XSS zranitelnosti
  6. Nepoužívejte GET požadavky pro operace, které mění stav
    1. 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”)
  • 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

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)
  • 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

  • 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 nebo Referer
  • 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 web example.com
  • 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 a Referer
    • Použijte X-Forwarded-Host hlavičku
      • Předejte problému s proxy serverem
      • Obsahuje původní hodnotu hlavičky Host
  • Ověření funguje správně, pokud jsou přítomny hlavičky Origin a Referer, 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í
    • Mechanismus autentizace a autorizace (heslo, MFA)
    • Jednorázový token
    • CAPTCHA
  • 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čku anti-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

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);
}
}
});

Kam dál