Jak poprawnie dodać token CSRF za pomocą PHP

Próbuję dodać pewne zabezpieczenia do formularzy na mojej stronie. Jeden z formularzy wykorzystuje AJAX, a drugi jest prostym formularzem" skontaktuj się z nami". Próbuję dodać token CSRF. Problem, który mam polega na tym, że token pojawia się tylko w HTML "wartość" przez jakiś czas. Przez resztę czasu wartość jest pusta. Oto kod, którego używam w formularzu AJAX:

PHP:

if (!isset($_SESSION)) {
    session_start();
$_SESSION['formStarted'] = true;
}
if (!isset($_SESSION['token']))
{$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;

}

HTML

 <form>
//...
<input type="hidden" name="token" value="<?php echo $token; ?>" />
//...
</form>
Jakieś sugestie?
Author: Ken, 2011-06-09

3 answers

Ostrzeżenie Bezpieczeństwa: md5(uniqid(rand(), TRUE)) nie jest bezpiecznym sposobem generowania liczb losowych. Zobacz ta ODPOWIEDŹ aby uzyskać więcej informacji i rozwiązanie, które wykorzystuje kryptograficznie Bezpieczny generator liczb losowych.

Wygląda na to, że potrzebujesz else ze swoim if.
if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}
 22
Author: datasage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:41

Dla kodu zabezpieczającego, proszę nie generować tokenów w ten sposób: $token = md5(uniqid(rand(), TRUE));

Wypróbuj to:

W tym celu należy skontaktować się z Działem obsługi klienta.]}

PHP 7

session_start();
if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];

Sidenote: jeden z projektów open source mojego pracodawcy jest inicjatywą backport random_bytes() i random_int() do Projekty PHP 5. Jest licencjonowany przez MIT i dostępny na Github i Composer jako paragonie/random_compat.

[[37]}PHP 5.3+ (lub z ext-mcrypt)
session_start();
if (empty($_SESSION['token'])) {
    if (function_exists('mcrypt_create_iv')) {
        $_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
    } else {
        $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
    }
}
$token = $_SESSION['token'];

Weryfikacja Tokena CSRF

Nie używaj tylko == czy nawet ===, używaj hash_equals() (PHP 5.6 + tylko, ale dostępne dla wcześniejszych wersji z biblioteką hash-compat ).

if (!empty($_POST['token'])) {
    if (hash_equals($_SESSION['token'], $_POST['token'])) {
         // Proceed to process the form data
    } else {
         // Log this as a warning and keep an eye on these attempts
    }
}

Idąc dalej za pomocą żetonów Per-Form

Możesz dodatkowo ograniczyć tokeny, aby były dostępne tylko dla konkretną formę za pomocą hash_hmac(). HMAC jest szczególną funkcją skrótu klawiszowego, która jest Bezpieczna w użyciu, nawet przy słabszych funkcjach skrótu (np. MD5). Polecam jednak zamiast tego używać funkcji hashowych z rodziny SHA-2.

Po pierwsze, Wygeneruj drugi token do użycia jako klucz HMAC, a następnie użyj logiki, aby go wyrenderować:]}
<input type="hidden" name="token" value="<?php
    echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />

A następnie Użycie operacji zgodnej podczas weryfikacji tokena:

$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
    // Continue...
}

Tokeny wygenerowane dla jednego formularza nie mogą być ponownie użyte w innym kontekst bez znajomości $_SESSION['second_token']. ważne jest, aby użyć oddzielnego tokena jako klucza HMAC niż ten, który właśnie upuszczasz na stronę.

Bonus: Podejście Hybrydowe + Integracja Gałązek

Każdy, kto korzysta z silnika szablonów gałązek, może skorzystać z uproszczonej podwójnej strategii, dodając ten filtr do środowiska gałązek:]}
$twigEnv->addFunction(
    new \Twig_SimpleFunction(
        'form_token',
        function($lock_to = null) {
            if (empty($_SESSION['token'])) {
                $_SESSION['token'] = bin2hex(random_bytes(32));
            }
            if (empty($_SESSION['token2'])) {
                $_SESSION['token2'] = random_bytes(32);
            }
            if (empty($lock_to)) {
                return $_SESSION['token'];
            }
            return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
        }
    )
);

Dzięki tej funkcji gałązki możesz używać zarówno tokenów ogólnego przeznaczenia, jak i tak:

<input type="hidden" name="token" value="{{ form_token() }}" />

Lub zamknięty wariant:

<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />

Twig zajmuje się tylko renderowaniem szablonów; nadal musisz poprawnie zweryfikować tokeny. Moim zdaniem strategia Twig oferuje większą elastyczność i prostotę, przy jednoczesnym zachowaniu możliwości maksymalnego bezpieczeństwa.


Jednorazowe tokeny CSRF

Jeśli masz wymóg bezpieczeństwa, aby każdy token CSRF mógł być użyty dokładnie raz, najprostsza strategia regeneruje go po każdej pomyślnej walidacji. Jednak uczynienie tego spowoduje Unieważnij każdy poprzedni token, który nie miesza się dobrze z osobami przeglądającymi wiele kart jednocześnie.

Paragon Initiative Enterprises prowadzi bibliotekę anty-CSRF dla tych narożnych przypadków. Działa wyłącznie z tokenami jednorazowego użytku. Gdy w danych sesji zostanie zapisana wystarczająca ilość Tokenów (domyślna konfiguracja: 65535), najpierw zostaną uruchomione najstarsze nieumeblowane tokeny.
 188
Author: Scott Arciszewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 13:03:54

Zmienna $token nie jest pobierana z sesji, gdy jest tam

 1
Author: Dani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-29 18:29:51