Hur implementerar jag CSRF-token?

Permalänk
Medlem

Hur implementerar jag CSRF-token?

Hej,
suttit ett tag och försökt förstå mig på hur jag implementerar CSRF-token på min hemsida. Har en hemsida som består av login, produktsida, varukorg. Kör PHP & MySQL. Jag förstår att man ska ha ett speciellt token som skapas och kollas varje gång du refreshar sidan men förstår inte hur detta går till i praktiken. Någon vänlig själ som kan hjälpa en förvirrad kille såhär en fredagskväll?

Visa signatur

CPU: Ryzen 5600xGPU: 1080 TI ROG Strix RAM:2x16GB G.skill Trident @ 3600MHz MoBo: Asus B550FPSU: Corsair SF750
En resa till Nordkorea
2 dagar i Tjernobyl

Permalänk
Geeks
Jobbar med data

Hej,

Först skulle jag rekommendera att du läser den långa, tråkiga, men informativa wikin från owasp.
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(C...

TLDR:
1. Inga XSS hål får finnas
2. Kontrollera Origin Header/Referer Header
3. Kontrollera CSRF token

De har själva släppt ett PHP bibliotek som löser följande problem:
https://github.com/mebjas/CSRF-Protector-PHP

Om du bygger din hemsida på ett ramverk, rekommenderar jag att du aktiverar CSRF den vägen istället. Alternativt kan man alltid låna mekaniken från ett ramverk t.ex. Codeigniter*.

* Använder sig utav $_COOKIE istället för $_SESSION, för att lättare plockas upp utav Javascript.

Grundera:
1. Skapa en unik token inuti din $_SESSION
2. Lägg in den i din form[POST]
3. Validera om de stämmer

Använder du Ajax för att spara information, behöver du säkra upp även den.

Permalänk
Medlem
Skrivet av jreklund:

Hej,

Först skulle jag rekommendera att du läser den långa, tråkiga, men informativa wikin från owasp.
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(C...

TLDR:
1. Inga XSS hål får finnas
2. Kontrollera Origin Header/Referer Header
3. Kontrollera CSRF token

De har själva släppt ett PHP bibliotek som löser följande problem:
https://github.com/mebjas/CSRF-Protector-PHP

Om du bygger din hemsida på ett ramverk, rekommenderar jag att du aktiverar CSRF den vägen istället. Alternativt kan man alltid låna mekaniken från ett ramverk t.ex. Codeigniter*.

* Använder sig utav $_COOKIE istället för $_SESSION, för att lättare plockas upp utav Javascript.

Grundera:
1. Skapa en unik token inuti din $_SESSION
2. Lägg in den i din form[POST]
3. Validera om de stämmer

Använder du Ajax för att spara information, behöver du säkra upp även den.

Tack för svar. Vi får dessvärre inte använda frameworks utan måste göra allt själv.

Den metoden vi behöver skydda använder GET istället för post och vi är lite osäkra på hur vi ska konvertera det till POST då (om jag förstår det rätt) man inte kan kolla origin header/CSRF token i en get. I detta fallet handlar det om att lägga till föremål i en kundvagn (notera att detta är i utbildningssyfte, inget "seriöst").

Visa signatur

CPU: Ryzen 5600xGPU: 1080 TI ROG Strix RAM:2x16GB G.skill Trident @ 3600MHz MoBo: Asus B550FPSU: Corsair SF750
En resa till Nordkorea
2 dagar i Tjernobyl

Permalänk
Geeks
Jobbar med data

Skillnaden mellan att skicka något som GET och POST är hur man öppnar formuläret.

<form action="URL" method="get">

<form action="URL" method="post">

Inuti form taggen placerar man CSRF token:

<form action="URL" method="post"> <input type="hidden" name="csrf_token_name" value="ajudiasdjsaidsaud8da878"> ... </form>

Går självklart att använda med GET också, om ni nu använder <a> taggar för att "Lägga till i kundkorgen". Det ser dock väldigt fult ut.

<a href="produkter.php?action=add&csrf_token_name=ajudiasdjsaidsaud8da878">Lägg till i kundkorgen</a>

<form action="URL" method="get"> <input type="hidden" name="csrf_token_name" value="ajudiasdjsaidsaud8da878"> ... </form>

Ni hämtar sedan csrf_token_name med hjälp utav $_GET eller $_POST och validerar den.

$valid = isset($_GET['csrf_token_name'], $_SESSION['csrf_token_name']) && hash_equals($_GET['csrf_token_name'], $_SESSION['csrf_token_name']); $valid = isset($_POST['csrf_token_name'], $_SESSION['csrf_token_name']) && hash_equals($_POST['csrf_token_name'], $_SESSION['csrf_token_name']);

Själva token måste genereras på ett säkert sätt och kan göras tillsammans med följande. (Codeigniter)

/** * Get random bytes * * @param int $length Output length * @return string */ public function get_random_bytes($length) { if (empty($length) OR ! ctype_digit((string) $length)) { return FALSE; } if (function_exists('random_bytes')) { try { // The cast is required to avoid TypeError return random_bytes((int) $length); } catch (Exception $e) { // If random_bytes() can't do the job, we can't either ... // There's no point in using fallbacks. log_message('error', $e->getMessage()); return FALSE; } } // Unfortunately, none of the following PRNGs is guaranteed to exist ... if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE) { return $output; } if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE) { // Try not to waste entropy ... is_php('5.4') && stream_set_chunk_size($fp, $length); $output = fread($fp, $length); fclose($fp); if ($output !== FALSE) { return $output; } } if (function_exists('openssl_random_pseudo_bytes')) { return openssl_random_pseudo_bytes($length); } return FALSE; }

Och används såhär:

$rand = $this->get_random_bytes(16); $rand = ($rand === FALSE) ? md5(uniqid(mt_rand(), TRUE)) : bin2hex($rand); $_SESSION['csrf_token_name'] = $rand;

Har ni mycket AJAX på sidan eller inte vill smutsa ner era formulär/länkar så är det Set-Cookie istället som gäller. Då jQuery m.m kan hämta upp den. Sätts med hjälp av setcookie() på alla sidor. http://php.net/manual/en/function.setcookie.php
Då behöver man ändra lite på $valid som jag skrev ovan, från $_SESSION till $_COOKIE.

Set-Cookie:csrf_token_name=ajudiasdjsaidsaud8da878; path=/; httponly