Novena Parte -> Manejo de usuarios
Para tratar de proteger las sesiones es conveniente que en cada cambio de estado (login y logout) generemos una nueva id de sesión y eliminemos las variables que no serán necesarias:
Código PHP:
Ver original
// Ingreso de usuario
function login($id, $usuario, $correo, $rol) {
// Se supone que ya verificamos que los datos son correctos
// Creamos una nueva id de sesión y eliminamos la anterior (true)
// Creamos sólo las variables que necesitaremos
'id' => $id,
'nombre' => $usuario,
'correo' => $correo,
'rol' => $rol
)
);
// Redirigimos a página de usuario
header('Location: bienvenido.php'); }
// Finalizar sesión
function logout() {
// Creamos una nueva id de sesión y eliminamos la anterior (true)
// Eliminamos información de sesión
// Redirigimos a página principal
header('Location: index.php'); }
Aunque el uso de md5() no es tan aconsejable, podemos darle un poco más de confiabilidad agregando información y, de paso, nos ayudará tratando de hacer más segura nuestra sesión:
Código PHP:
Ver original
// Un pequeño agregado para usar en md5()
DEFINE('SALT_MD5', 'AbC%"$%"');
// El usuario ya inició sesión?
if(esUsuario()) {
// Ok, es una sesión válida
// Aquí el código para usuarios con sesión iniciada
} else {
// No ha iniciado sesión
// Aquí el código para "invitados"
}
// Obtener cadena de comprobación
function strMd5($cadena) {
return md5(SALT_MD5
. $cadena . SALT_MD5
); }
// Ingreso de usuario
function login($id, $usuario, $correo, $rol) {
// Creamos sólo las variables que necesitaremos
'id' => $id,
'nombre' => $usuario,
'correo' => $correo,
'rol' => $rol,
'comprobacion' => strMd5("$id|$usuario|$correo|{$_SERVER['REMOTE_ADDR']}");
)
);
// Redirigimos a página de usuario
header('Location: bienvenido.php'); }
// Finalizar sesión
function logout() {
// Creamos una nueva id de sesión y eliminamos la anterior (true)
// Eliminamos información de sesión
// Redirigimos a página principal
header('Location: index.php'); }
// Comprobar si es una sesión válida
// Devuelve verdadero sólo si el usuario inició sesión y la cadena de comprobación es correcta
// Si el usuario cambia de IP, entonces finalizará su sesión
function esUsuario() {
if(isset($_SESSION['usuario'])) { $cadena = "{$_SESSION['usuario']['id']}|{$_SESSION['usuario']['nombre']}|{$_SESSION['usuario']['correo']}|{$_SERVER['REMOTE_ADDR']}";
if(strMd5($cadena) == $_SESSION['usuario']['comprobacion']) {
// Sesión válida
return true;
}
// Sesión no válida, posiblemente es un ataque
// Cerramos sesión
logout();
}
return false;
}
Nota 1: La dirección IP es tan poco confiable como la información del navegador, sin embargo, los datos añadidos en la cadena de comprobación harán que un atacante se tenga que esforzar más para obtener acceso al sitio.
Nota 2: Sí, lo sé, puede resultar molesto que la sesión caduque automáticamente cuando encendemos la computadora y regresamos al sitio, todo porque la IP cambió, entonces, esta estrategia podría ser usada sólo para usuarios con permisos administrativos.
Hay que tener en cuenta que la sesión podría cerrarse aún cuando el usuario tenga abierta la página, pero esté inactivo durante mucho tiempo; igualmente, debería haber la posibilidad de guardar los datos del usuario para mantener la conexión activa entre cada visita. En estos casos, debemos hacer uso de cookies, teniendo cuidado de guardar sólo información que nos permita obtener id de usuario y comparar con otros datos para saber que se trata de una sesión válida.
Más o menos así quedaría el script al final:
Código PHP:
Ver original
// Un pequeño agregado para usar en md5()
DEFINE('SALT_MD5', 'AbC%"$%"');
// El usuario ya inició sesión?
if(esUsuario()) {
// Ok, es una sesión válida
// Aquí el código para usuarios con sesión iniciada
} else {
// No ha iniciado sesión
// Aquí el código para "invitados"
}
// Obtener cadena de comprobación
function strMd5($cadena) {
return md5(SALT_MD5
. $cadena . SALT_MD5
); }
// Ingreso de usuario
function login($id, $usuario, $correo, $rol) {
// Creamos sólo las variables que necesitaremos
'id' => $id,
'nombre' => $usuario,
'correo' => $correo,
'rol' => $rol,
'comprobacion' => strMd5("$id|$usuario|$correo|{$_SERVER['REMOTE_ADDR']}")
)
);
// Creamos cookie 'usuario' con id y cadena de comprobación
// Expira en un mes y está disponible en la raiz del sitio y todas las subcarpetas
setcookie('usuario', "$id|{$_SESSION['usuario']['comprobacion']}", time() + (86400 * 30), '/');
// Redirigimos a página de usuario
header('Location: bienvenido.php'); }
// Finalizar sesión
function logout() {
// Creamos una nueva id de sesión y eliminamos la anterior (true)
// Eliminamos información de sesión
// Borramos info de cookie
// Redirigimos a página principal
header('Location: index.php'); }
// Comprobar si es una sesión válida
// Devuelve verdadero sólo si el usuario inició sesión y la cadena de comprobación es correcta
// Si el usuario cambia de IP, entonces finalizará su sesión
function esUsuario() {
// Si no existe la variable de sesión
if( ! isset($_SESSION['usuario'])) { // Entonces buscamos en cookie
$cookie = (isset($_COOKIE['usuario'])) ?
$_COOKIE['usuario'] : 'sin sesión'; if($cookie != 'sin sesión' && $cookie != '') {
// Separamos ud y cadena de comprobación
// Sólo será válida si son dos elementos
$id = (int) $tmp[0];
$sql = "SELECT id, nombre, correo, rol FROM usuarios WHERE id = $id";
// Leemos la consulta con nuestra librería preferida
// Si los datos son válidos, creamos variables de sesión
$_SESSION['usuario'] = array( 'id' => $id,
'nombre' => $usuario,
'correo' => $correo,
'email' => $email,
'rol' => $rol,
'comprobacion' => $tmp[1] // La cadena obtenida de cookie
);
}
}
}
if(isset($_SESSION['usuario'])) { $cadena = "{$_SESSION['usuario']['id']}|{$_SESSION['usuario']['nombre']}|{$_SESSION['usuario']['correo']}|{$_SERVER['REMOTE_ADDR']}";
if(strMd5($cadena) == $_SESSION['usuario']['comprobacion']) {
// Sesión válida, renovamos cookie para mantenerlo conectado
setcookie('usuario', "$id|{$_SESSION['usuario']['comprobacion']}", time() + (86400 * 30), '/');
return true;
}
// Sesión no válida, posiblemente es un ataque
// Cerramos sesión
logout();
}
// No hay sesión válida, nos aseguramos de que la cookie siga sin datos
return false;
}
Últimas recomendaciones:
- Nunca guardar las contraseñas directamente en la base de datos, es recomendable codificarla con md5(), sha1() o cualquier otro método que no permita decodificar y agregando algunos caracteres como en el ejemplo anterior, que usamos la constante SALT_MD5.
- Si en el ejemplo anterior cambiamos la cadena de comprobación quitando el correo y agregando la contraseña codificada nos facilitaría comprobar si la cookie de usario es realmente válida.
- Por favor, no copies y pegues el código esperando que funcione, todo lo hice en el bloc de notas y, seguramente, habrá muchos errores; analiza, entiende y adapta lo que creas que te pueda servir.
- Cualquier duda será respondida en este mismo tema.
Bueno, creo que es todo, sólo falta aclarar que no hay sistema 100% seguro y que esto es sólo una guía para tratar de evitar ataques y cualquier comentario, sugerencia o añadidura será bienvenida.