Para el código de seguridad, no genere sus tokens de esta manera: $token = md5(uniqid(rand(), TRUE));
Probar esto:
Generando un token CSRF
PHP 7
session_start();
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
Nota al margen: Uno de los proyectos de código abierto de mi empleador es una iniciativa de backport random_bytes()
y random_int()
en proyectos PHP 5. Tiene licencia del MIT y está disponible en Github y Composer como paragonie / random_compat .
PHP 5.3+ (o con 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'];
Verificación del token CSRF
No solo use ==
o incluso ===
use hash_equals()
(solo PHP 5.6+, pero disponible para versiones anteriores con la biblioteca 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
}
}
Yendo más lejos con tokens por formulario
Puede restringir aún más los tokens para que solo estén disponibles para un formulario en particular usando hash_hmac()
. HMAC es una función hash con clave particular que es segura de usar, incluso con funciones hash más débiles (por ejemplo, MD5). Sin embargo, recomiendo usar la familia SHA-2 de funciones hash en su lugar.
Primero, genere un segundo token para usar como clave HMAC, luego use una lógica como esta para representarlo:
<input type="hidden" name="token" value="<?php
echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
Y luego usando una operación congruente al verificar el token:
$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
// Continue...
}
Los tokens generados para un formulario no se pueden reutilizar en otro contexto sin saberlo $_SESSION['second_token']
. Es importante que utilice un token diferente como clave HMAC que el que acaba de colocar en la página.
Bono: Enfoque híbrido + Integración Twig
Cualquiera que use el motor de plantillas Twig puede beneficiarse de una estrategia dual simplificada al agregar este filtro a su entorno 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']);
}
)
);
Con esta función Twig, puede usar ambos tokens de propósito general así:
<input type="hidden" name="token" value="{{ form_token() }}" />
O la variante bloqueada:
<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
Twig solo se ocupa del renderizado de plantillas; aún debe validar los tokens correctamente. En mi opinión, la estrategia Twig ofrece una mayor flexibilidad y simplicidad, al tiempo que mantiene la posibilidad de máxima seguridad.
Tokens CSRF de un solo uso
Si tiene un requisito de seguridad de que cada token CSRF se pueda usar exactamente una vez, la estrategia más simple lo regenerará después de cada validación exitosa. Sin embargo, hacerlo invalidará todos los tokens anteriores, lo que no se combina bien con las personas que navegan por varias pestañas a la vez.
Paragon Initiative Enterprises mantiene una biblioteca Anti-CSRF para estos casos de esquina. Funciona con tokens de un solo uso por formulario, exclusivamente. Cuando se almacenan suficientes tokens en los datos de la sesión (configuración predeterminada: 65535), se eliminarán primero los tokens no canjeados más antiguos.
token_time
para qué se usa?