Secretos en php

diciembre 3, 2014

Cualquier web con usuarios registrados debe contar con la funcionalidad de “¿Olvido su contraseña?”.
Implementar dicha funcionalidad es muy sencillo. Veamos: mandaremos al usuario un email con la url de login, seguida de un código.
Por ejemplo: http://www.example.com/login/1.
Esta dirección daría acceso al usuario 1. Problema: es perfectamente visible. Añadamos por tanto, un poco de md5 para enmarañar la url que quedaria asi:
http://www.example.com/login/c4ca4238a0b923820dcc509a6f75849b
Ya no es visible, pero presenta dos problemas: es vulnerable a ataques por diccionario, y ataques de fortuna (alguien puede probar a introducir http://www.example.com/login/ + md5(123).

Solución: combinamos la id del usuario, un campo del propio del usuario (fecha de alta por ejemplo), con una semilla elegida al azar mas la marca de tiempo.
http://www.example.com/login/ + md5( $id . $f_alta . “jhdjdhhj “. date(“Ymd”));

Esta sencilla clave es única para un usuario con determinda id y fecha de alta, para cierta aplicación y para cierto periodo de tiempo (el día de hoy).

Ahora ya podemos incluir el código de nuestra aplicación de ‘palabras secretas’. Os adjunto un sencillo pseudo-codigo:


const SEED = "uiusiui j"; // cualquier valor escogido al azar. Hazlo único para cada instalación.
const SECRET_VALID ="Ymd"; // notación date de PHP.

function semilla_secreta(){
return SEED . date(SECRET_VALID);
}

function secreto( $datos ) {
return md5(implode($datos) . semilla_secreta());
}

// devuelve la id del usuario al que corresponde el secreto.
function check_secreto ($clave) {
$sql = sprintf("SELECT id FROM usuarios WHERE MD5(Concat(id,alta,'%s'))='%s',
addslashes(semilla_secreta()),
addslashes($clave));
return query_var($sql); // debes implementar esta función.
}

// enviar el secreto a cierto email,
function enviar_secreto($email) {
// buscamos los datos (id y alta) de un usuario con el email dado.
$sql = sprintf("SELECT id,alta FROM usuarios WHERE email='%s', addslashes($email))
$datos = query_registro($sql); // debes implementar esta funcion que debe devolver un array.
if ( $datos ) {
...
$mensaje .= sprintf("click aqui", secreto($datos));
mail($email,"Acceso.", $mensaje);
}
}

PD: para los más paranoicos.
Este login todavía es vulnerable a un ataque de fuerza bruta, pero con una posibilidad muy baja de éxito 1 entre 2**128 para cada petición.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: