NemProgrammering.dk logo

Beskyt din hjemmeside mod CSRF

  Skrevet af: Kenneth     16-03-2014     Skrevet i: PHP

Så er jeg tilbage med en ny guide til jer. Denne gang skal vi kigge på CSRF, som er en forkortelse for Croos Site Request Forgery.

CSRF foregår gennem skadelig kode der afvikles fra et andet websted og på den måde kan de få adgang til brugerens åbne session på dit website og udføre skadelige handlinger via formularer på dit website.

Dette vil vi helst forhindre. Derfor indfører vi CSRF-beskyttelse. De fleste store CMS-løsninger såsom Drupal, Magento, Prestashop osv. anvender allerede en eller anden form for beskyttelse mod CSRF.

Tokens

Det vi vil kigge på er tokens. Tokens er en tilfældig sammensat streng af bogstaver og tal som tilsammen er din nøgle til at indsende en udfyldt formular.

For implementere tokens på en fornuftig måde og for nemt at kunne opdatere din CSRF-beskyttelse på en nem måde anbefaler jeg at du anvender objekt orienteret programmering. Det er en god idé at se de videoer som ligger her på nemprogrammering.dk inden du går igang med at følge denne guide.

Vi starter med at oprette en fil til vores objektklasse. Da man ofte vil samle flere klasser der hænger sammen, så vil det være en god idé at gemme filen i en mappe til sikkerhedsrelaterede funktioner.

For at gøre det nemt har jeg kaldt min fil csrf.class.php.
Start med at lave vores klasse.

<?php
class Csrf {

}
?>

Som det næste tilføjer vi en funktion der skal oprette et ID til vores token, således at vi kan finde den igen. Vi vil også tilføje et par funktioner der skal hjælpe med at oprette dette ID.

<?php
class Csrf {
    public function get_token_id() {
        if(isset($_SESSION['token_id'])) {
            return $_SESSION['token_id'];
        }
        else {
            $token_id = $this->random(10);
            $_SESSION['token_id'] = $token_id;
            return $token_id;
        }
    }
}
?>

Den funktion vi lige har lavet vil først tjekke om brugeren allerede har et token_id sat i $_SESSION. Findes det ikke vil den lave et nyt ID til brugeren og gemme det i session. Til sidst returneres det oprettede ID.

private function random($len) {
    if (@is_readable('/dev/urandom')) {
            $f=fopen('/dev/urandom', 'r');
            $urandom=fread($f, $len);
            fclose($f);
    }

    $return='';
    for ($i=0;$i<$len;++$i) {
        if (!isset($urandom)) {
            if ($i%2==0) mt_srand(time()%2147 * 1000000 + (double)microtime() * 1000000);
            $rand=48+mt_rand()%64;
        } else $rand=48+ord($urandom[$i])%64;

        if ($rand>57)
            $rand+=7;
        if ($rand>90)
            $rand+=6;

        if ($rand==123) $rand=52;
        if ($rand==124) $rand=53;
        $return.=chr($rand);
    }
    return $return;
}

Funktionen random() vil oprette en tilfældig streng vha. webserverens egen funktion til at oprette tilfældige blandinger af tal og bogstaver.

public function get_token() {
    if(isset($_SESSION['token_id'])) {
        if(isset($_SESSION['token_value'])) {
            return $_SESSION['token_value'];
        } else {
            $token = hash('sha256', $this->random(500));
            $_SESSION['token_value'] = $token;
            return $token;
        }
    }
    else {
        $this->get_token_id();
        $this->get_token();
    }
}

Som det næste laver vi en kopi af funktionen get_token_id() som vi vil bruge til at oprette vores token. Funktionen kalder vi get_token().
Det sidste vil nu mangler at gøre er at oprette en funktion der varetager validering af $_POST eller $_GET når formularen skal behandles af vores PHP-kode der normalt vil hente data fra enten en fil eller en SQL-database.

public function check_valid($method) {
    if($method == 'post' || $method == 'get') {
        $post = $_POST;
        $get = $_GET;
        if(isset(${$method}[$this->get_token_id()]) && (${$method}[$this->get_token_id()] == $this->get_token())) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

Nu hvor vi har vores tokens på plads skal vi have det sat i brug således at vi kan begynde at have gavn af vores CSRF-beskyttelse.
I nedenstående kode finder I en formular og noget PHP-kode. Dette skal eksistere i samme fil.

<?php
session_start();
include 'csrf.class.php';
$csrf = new csrf();

//Hent token_id og token
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);
?>

Herefter kommer hele vores HTML og vores formular

<form name="kontakt" method="post" action="submit.php">
<input type="text" name="navn" placeholder="Navn">
<input type="text" name="emne" placeholder="Emne">
<input type="email" name="email" placeholder="eksempel@eksempel.dk">
<button type="submit">Send besked</button>
</form>

I filen submit.php skal vi have bygget validering af CSRF token_id og CSRF token. Dertil bruger vi funktionen check_valid().
Filen submit.php kommer til at se således ud:

<?php
session_start();
include 'csrf.class.php';
$csrf = new csrf();

//Hent token_id og token
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);

if($csrf->check_valid('post')) {
    //Udfør submit af formular
}
?>

Vi anvender ikke noget else-statement da vi ikke ønsker, at personer der forsøger at skade vores website, skal vide at vi anvender tokens. Ved at vi ikke gør noget vil den simpelthen fortsætte med at udføre koden udenfor vores if-statement.

Spørgsmål og kommentarer

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.