Permisos por rol
Estar logueado no es lo mismo que poder hacer todo. En la tienda hay vendedores y administradores: los dos entran, pero el vendedor carga ventas y el administrador, además, cambia precios y ve reportes.
El rol se guarda en la sesión al iniciar sesión, y cada acción del área privada lo comprueba:
if ($_SESSION['rol'] !== 'administrador') {
http_response_code(403);
exit('No tenés permiso para esta acción');
}
La comprobación va en el servidor, siempre. Esconder un botón en el HTML no protege nada: cualquiera puede pedir la dirección a mano. El botón oculto es comodidad; el permiso en el servidor es seguridad.
Contraseñas
La contraseña no se guarda: se guarda su hash, que es irreversible. Al crear el usuario se hashea; al iniciar sesión se compara con password_verify():
$hash = password_hash($clave, PASSWORD_DEFAULT);
if (password_verify($clave, $fila['clave'])) {
// la contraseña es correcta
}
Nunca MD5 ni SHA1: son rápidos y sirven para verificar archivos, no contraseñas. bcrypt (lo que usa PASSWORD_DEFAULT) está hecho para ser lento, y eso frena a quien prueba combinaciones.
Inyección SQL
Si el valor del usuario se pega en el texto del SQL, puede traer SQL adentro. Con ' OR '1'='1 en un campo de login, una consulta mal armada devuelve todos los usuarios y deja entrar a cualquiera.
// VULNERABLE
$sql = "SELECT * FROM usuarios WHERE usuario = '$usuario'";
// SEGURO
$consulta = $pdo->prepare('SELECT * FROM usuarios WHERE usuario = ?');
$consulta->execute([$usuario]);
La consulta preparada separa el código del valor: el valor nunca se interpreta como SQL. Es la única forma correcta de armar consultas con datos de afuera.
XSS
XSS (cross-site scripting) es inyectar HTML o JavaScript en una página que otros van a ver. Si un comentario con <script> se guarda y se muestra tal cual, se ejecuta en el navegador de cada visitante.
La defensa es escapar al mostrar:
echo htmlspecialchars($comentario, ENT_QUOTES, 'UTF-8');
Se escapa al mostrar, no al guardar: guardar entidades ensucia la base y rompe cualquier otro uso del dato. Y se escapa todo lo que venga de afuera, incluido lo que salió de la propia base de datos.
CSRF
CSRF (cross-site request forgery) es que otra página haga que el usuario envíe un formulario sin darse cuenta: la sesión ya está abierta, así que el servidor lo acepta. La defensa es un token que solo conoce el sitio:
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token;
// en el formulario
echo '<input type="hidden" name="csrf" value="' . $token . '">';
// al recibir
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
http_response_code(419);
exit('Token inválido');
}
El token se genera por sesión, viaja en un campo oculto y se compara con hash_equals(), que tarda lo mismo acierte o falle y no filtra información por tiempos.
Resumen
- Permisos: el rol se comprueba en el servidor, no escondiendo botones.
- Contraseñas:
password_hashypassword_verify; nunca MD5 ni SHA1. - Inyección SQL: siempre consultas preparadas; el valor viaja aparte.
- XSS:
htmlspecialcharsal mostrar todo lo que venga de afuera. - CSRF: token por sesión en cada formulario, comparado con
hash_equals.
En la última lección se junta todo: la tienda completa, con login, catálogo, ventas y reportes, funcionando en XAMPP.
Licencia. El material de esta página es elaboración propia de Prof Mauro Bobyk —a partir de las fuentes que detalla el pie del sitio— y se publica bajo una licencia Creative Commons BY-NC-SA 4.0.