Con este pequeño sistema se espera que los ataques CSRF ya no nos afecten, este es el segundo aporte que hago. El primero era un contador de llaves donde se podía ingresar un código y el programa iba a devolver la cantidad de llaves abiertas y cerradas.
Con esto quiero decir que este es mi primer aporte "avanzado" para mi así que por eso muy posiblemente existan fallos o áreas donde el código pueda ser mejorado.
El sistema lo que hace es crear tokens de seguridad de clave única cifrada, yo lo uso para evitar ataques en las paginas donde existan formularios, donde se use $_GET como por ejemplo un perfil (profile.php?id_user=x) y también lo uso en ficheros AJAX (realmente no estoy seguro si es necesario pero quería que en todas las partes de mi aplicación web donde se ejecuten funciones importantes exista este control de seguridad)
Con este código espero que me lluevan criticas, sugerencias para que podamos mejorarlo entre todos.
En principio creo 6 funciones, creo que esta de más decir que uso PHP para esto pero quería aclararlo.
La primera función servirá para crear un código de 22 caracteres unos 20 números + 2 letras en mayúsculas.
Código
function Create_Code() { $result = ($random_code_A).($year_code).($month_code).($day_code).($hour_code).($minute_code).($second_code).($random_code_B).($random_code_C); return $result; }
La segunda función servirá para crear un token determinado para cada pagina que exista en nuestra aplicación.
Código
function Create_Token($page) { { { $token_csrf = Create_Code(); // Creamos un codigo unico $token_csrf = Hash_Data($token_csrf); // ciframos el codigo lo que nos va a dar una cadena de 60 caracteres $_SESSION['token_'.($page)] = $token_csrf; // se crea una session con el token generado } } }
La funciòn hash_data (me había olvidado de ponerla, gracias por el aviso!)
Código
function Hash_Data($data) // El tipo de cifrado es general, con esto quiero decir que uso esta función para cifrar contraseñas, códigos de activación de un email, etcétera, ustedes pueden usar el que quieran pero en mi aplicación como norma general uso este. { $result = 'Error'; { $result = password_hash($data, PASSWORD_BCRYPT); } return $result; }
La tercera función servirá para crear una lista de tokens (acá van a tener que poner todas las paginas que van a usar tokens, donde usen formularios, peticiones $_get, o ajax)
Código
function Create_Tokens() { // Ajax Create_Token('ajax'); // acá recomiendo que creen un solo token para validar ficheros ajax // Pages ($_POST or $_GET) Create_Token('login'); // este token pertenece a login.php donde estará el formulario de acceso }
La cuarta función servirá para validar un token determinado.
Código
function Validate_Token($page, $value) { $result = false; { { { $result = true; } } } return $result; }
Ahora vamos a ponerlo en práctica, por supuesto todas las funciones de arriba deberían ponerlas en una libreria que ustedes pueden crear e incluirla en todas sus paginas php incluyendo "Create_Tokens();" pero para este ejemplo vamos a dejarlo acá.
Código
<!-- login.php --> <?php Create_Tokens(); ?> <form method="post"> <input type="text" name="el_texto"></input> <input type="hidden" name="token_csrf" value="<?php echo($_SESSION['token_login']); ?>"> // Acá va a ir su token, fijense que le puse "token_login" esto tiene que ir personalizado dependiendo en que paginas lo usen <button type="submit" name="el_submit"></button> </form> <?php if(isset($_POST['el_submit'])) // cuando se envia el formulario { if(isset($_POST['token_csrf']) && Validate_Token('login', $_POST['token_csrf'])) { echo "mandaste ".$_POST['el_texto']; } else { echo "Se detecto un ataque CSRF"; } } ?>
Bueno ahora vamos a aplicar esto a una pagina que use $_GET como por ejemplo un perfil.
Código
<!-- profile.php --> <?php if(isset($_GET['id_user']) && isset($_GET['token_csrf'])) { $_GET['token_csrf'] = urldecode($_GET['token_csrf']); if(Validate_Token('token_profile', $_GET['token_csrf'])) { [Ejecución] } else { [Interrumpimos, redirigimos, etcétera] // ya que no es valido el token } } else { [Interrumpimos, redirigimos, etcétera] } ?>
Cuando vayamos a redirigir a el usuario a una pagina donde se use $_get debe de ser así...
Código
window.location.href = 'profile.php?id_user=<?php echo urlencode($id); ?>&token_csrf=<?php echo urlencode($_SESSION['token_profile']); ?>';
En ajax sería así:
Código
// ajax/fichero.php if(Validate_Token('token_ajax', $_POST['token_csrf'])) // $_POST['token_csrf'] depende de si en ajax ustedes usan GET o en este caso POST { [Ejecución] } else { [Interrupción] }
Espero que les guste y si lo quieren usar que me comenté si les gusto, escucho criticas como ya dije y entre todos poder ir mejorando este sistema. Gracias!