Próbuję trochę zabezpieczyć formularze w mojej witrynie. Jeden z formularzy korzysta z technologii AJAX, a drugi to prosty formularz „skontaktuj się z nami”. Próbuję dodać token CSRF. Problem, który mam, polega na tym, że token pojawia się tylko czasami w „wartości” HTML. 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?
token_time
służy?token_time
. Zamierzałem ograniczyć czas, w którym token jest ważny, ale nie zaimplementowałem jeszcze w pełni kodu. Dla jasności usunąłem to z powyższego pytania.Odpowiedzi:
W przypadku kodu zabezpieczającego nie generuj swoich tokenów w ten sposób:
$token = md5(uniqid(rand(), TRUE));
rand()
jest przewidywalnauniqid()
sumuje się tylko do 29 bitów entropiimd5()
nie dodaje entropii, po prostu miesza ją deterministycznieWypró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()
irandom_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żywajhash_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'])) { // Proceed to process the form data } else { // Log this as a warning and keep an eye on these attempts } }
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'])) { // Continue... }
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.
źródło
Wygląda na to, że potrzebujesz innego ze swoim if.
if (!isset($_SESSION['token'])) { $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token; $_SESSION['token_time'] = time(); } else { $token = $_SESSION['token']; }
źródło
md5(uniqid(rand(), TRUE));
kontekstom bezpieczeństwa.Zmienna
$token
nie jest pobierana z sesji, gdy się tam znajdujeźródło