W przypadku kodu zabezpieczającego nie generuj swoich tokenów w ten sposób: $token = md5(uniqid(rand(), TRUE));
Wypróbuj to:
Generowanie tokena CSRF
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
Uwaga: Jeden z projektów open source mojego pracodawcy to inicjatywa dotycząca backportów random_bytes()
i random_int()
projektów PHP 5. Jest licencjonowany przez MIT i dostępny w Github i Composer jako paragonie / random_compat .
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 tylko używaj ==
lub nawet ===
używaj hash_equals()
(tylko PHP 5.6+, ale dostępne we wcześniejszych wersjach z biblioteką kompatybilną z hash ).
if (!empty($_POST['token'])) {
if (hash_equals($_SESSION['token'], $_POST['token'])) {
} else {
}
}
Idąc dalej z tokenami zależnymi od formy
Możesz dodatkowo ograniczyć tokeny, aby były dostępne tylko dla określonego formularza, używając hash_hmac()
. HMAC to szczególna funkcja skrótu z kluczem, która jest bezpieczna w użyciu, nawet w przypadku słabszych funkcji skrótu (np. MD5). Jednak zamiast tego zalecam używanie rodziny funkcji skrótu SHA-2.
Najpierw wygeneruj drugi token do użycia jako klucz HMAC, a następnie użyj logiki takiej jak ta, aby go wyrenderować:
<input type="hidden" name="token" value="<?php
echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
A następnie używając kongruentnej operacji podczas weryfikacji tokena:
$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
}
Tokenów wygenerowanych dla jednego formularza nie można ponownie wykorzystać w innym kontekście bez wiedzy $_SESSION['second_token']
. Ważne jest, aby użyć oddzielnego tokena jako klucza HMAC niż ten, który właśnie upuścisz na stronie.
Bonus: podejście hybrydowe + integracja Twig
Każdy, kto korzysta z silnika tworzenia szablonów Twig, może skorzystać z uproszczonej strategii dualnej, dodając ten filtr do swojego środowiska Twig:
$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 Twig możesz używać obu tokenów ogólnego przeznaczenia w następujący sposób:
<input type="hidden" name="token" value="{{ form_token() }}" />
Lub wariant zamknięty:
<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 wymaganie dotyczące bezpieczeństwa, aby każdy token CSRF mógł być użyty dokładnie raz, najprostsza strategia generuje go ponownie po każdej pomyślnej weryfikacji. Jednak spowoduje to unieważnienie każdego poprzedniego tokena, który nie pasuje do osób, które przeglądają wiele kart jednocześnie.
Paragon Initiative Enterprises utrzymuje bibliotekę Anti-CSRF dla tych narożnych przypadków. Działa wyłącznie z tokenami jednorazowego użytku na formularz. Gdy wystarczająca liczba tokenów jest przechowywana w danych sesji (domyślna konfiguracja: 65535), najpierw wyłączy najstarsze niewykupione tokeny.
token_time
służy?