3.4Seguridad : inyección SQL, XSS, CSRF, hash
Lo esencial en 30 segundos
- Nunca confiar en las entradas : todo dato exterior es sospechoso — validar, luego tratar según el contexto (SQL, HTML, email).
- Tres fallas, tres reflejos : inyección SQL → consultas preparadas ; XSS → htmlspecialchars() en la visualización ; CSRF → token único verificado + SameSite.
- Contraseñas nunca en claro : password_hash() al almacenar, password_verify() al conectar — ninguna contraseña en duro en el código.
Mini-caso hilado — Riad audita su tienda antes de publicar
Riad, desarrollador web en Constantine, se dispone a publicar su tienda : comentarios mostrados en bruto, formulario de pedido sin token, contraseñas de clientes en claro en la base. Lo corrige todo : htmlspecialchars() en cada visualización, token CSRF único por formulario verificado al enviar, password_hash() al registrar y password_verify() al conectar. La auditoría sale en verde antes de la apertura.
Último episodio del hilo Riad — de la base a los objetos hasta el blindaje completo.
1. Inyección SQL, XSS, CSRF : mecanismos y defensas
La seguridad es permanente, no una opción final, alrededor del principio : NUNCA confiar en las entradas. (1) La inyección SQL (vista en 3.1) : SQL inyectado vía datos concatenados — defensa : consultas preparadas PDO, siempre. (2) El XSS : JavaScript inyectado que se ejecuta en el navegador ajeno cuando se muestran datos no fiables sin escapar (robo de sesión, defacement) — defensa : escapar en la visualización con htmlspecialchars(), siempre, para todo dato no fiable.
(3) El CSRF : forzar a un usuario conectado a ejecutar una acción sin saberlo vía una petición forjada desde otro sitio — defensa : tokens CSRF (valor único y secreto por formulario, verificado al enviar) y cookie SameSite. Riad añade un token generado por random_bytes() en campo oculto de cada formulario sensible y lo compara en sesión antes de todo tratamiento.
2. Hash, HTTPS, secretos : la higiene no negociable
Las contraseñas se almacenan HASHEADAS, nunca en claro : password_hash() al registrar (algoritmo seguro, sal y coste integrados), password_verify() al conectar. Ninguna contraseña en claro ni en duro en el código, las capturas o los mensajes — no es negociable.
El resto de la higiene : HTTPS en producción (cifrar), secretos fuera del código (claves y contraseñas de base en variables de entorno), errores detallados nunca mostrados en producción (registrados, no divulgados), PHP y dependencias al día, autorización verificada (no solo la autenticación). Para formarse : el OWASP Top 10, referencia de las fallas web.
🔎 Ejemplo concreto — XSS neutralizado, CSRF verificado, contraseña hasheada
Riad asegura la visualización de un apodo, un formulario y el registro.
Hola, ' . htmlspecialchars($pseudo, ENT_QUOTES, 'UTF-8') . '';
// 2) CSRF : token único en sesión, campo oculto, verificación
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
// en el formulario :
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
http_response_code(403);
exit('Solicitud rechazada.');
}
// 3) Hash : nunca en claro
$hash = password_hash($motDePasseSaisi, PASSWORD_DEFAULT);
// ... almacenamiento de $hash en base mediante consulta preparada ...
$ok = password_verify($motDePasseSaisi, $hashStocke);
Lectura : escape en la visualización, token comparado en hash_equals, hash y luego verificación.
Lección : tres gestos cortos que cierran tres fallas graves.
Vocabulario de la sección
- Nunca confiar en las entradas
- Principio transversal : todo dato exterior es potencialmente malicioso — validar y luego tratar según el contexto.
Ejemplo sencillo: Riad valida el email antes de todo uso.
Lo que no es: mostrar tal cual « porque viene de mi formulario ».
Reflejo: sospechoso por defecto, validado antes de usar.
- Inyección SQL → preparadas
- SQL inyectado vía concatenación ; defensa : siempre consultas preparadas PDO, nunca concatenación.
Ejemplo sencillo: El prepare/execute de la tienda de Riad.
Lo que no es: una concatenación « provisional ».
Reflejo: marcadores + execute, sin excepción.
- XSS → htmlspecialchars
- Script inyectado ejecutado en otros visitantes ; defensa : escapar todo dato no fiable en la visualización.
Ejemplo sencillo: El apodo comentado mostrado como texto inofensivo.
Lo que no es: un echo directo de un campo de usuario.
Reflejo: htmlspecialchars() en cada visualización no fiable.
- CSRF → tokens
- Acción forzada sin saberlo en un conectado ; defensa : token único por formulario verificado + cookie SameSite.
Ejemplo sencillo: El campo oculto comparado en sesión en casa de Riad.
Lo que no es: un formulario sensible sin ningún token.
Reflejo: token en cada acción sensible.
- Hash / HTTPS / secretos
- Contraseñas hasheadas (password_hash/verify, nunca en claro) ; HTTPS ; secretos en entorno ; errores ocultos en prod ; al día.
Ejemplo sencillo: El registro hasheado y la conexión verificada de Riad.
Lo que no es: una contraseña almacenada legible en base.
Reflejo: ningún secreto en claro, en ningún parte.
En la práctica — Asegurar una aplicación PHP
- Contra la inyección SQL : SIEMPRE consultas preparadas (PDO) — verificad que ningún dato esté concatenado en el SQL.
- Contra el XSS : escapad todo dato mostrado con htmlspecialchars() (comentarios, apodos, datos de base).
- Contra el CSRF : añadid un token CSRF (único, secreto) en vuestros formularios y verificadlo al enviar ; usad SameSite en las cookies.
- Hashead las contraseñas (password_hash/verify), usad HTTPS, gestionad los secretos fuera del código, no mostréis los errores en producción, mantened PHP/dependencias al día.
Guion listo para decir — presentar su auditoría de seguridad (30 s)
« Antes de publicar : consultas preparadas en todas partes, htmlspecialchars en cada visualización, token CSRF verificado en los formularios sensibles, contraseñas hasheadas con password_hash y verificadas con password_verify. Secretos en entorno, HTTPS en producción. »
Para decir en revisión prelanzamiento ; mostrar los cuatro gestos en el código.
Modelo copiar-pegar — registro y conexión seguros
prepare('INSERT INTO utilisateurs (email, mot_de_passe) VALUES (:email, :hash)');
$stmt->execute(['email' => $email, 'hash' => $hash]);
// Conexión : leer en preparada, verificar el hash
$stmt = $pdo->prepare('SELECT id, mot_de_passe FROM utilisateurs WHERE email = :email');
$stmt->execute(['email' => $emailSaisi]);
$utilisateur = $stmt->fetch();
if ($utilisateur && password_verify($motDePasseSaisi, $utilisateur['mot_de_passe'])) {
session_regenerate_id(true);
$_SESSION['utilisateur_id'] = $utilisateur['id'];
}
// Visualización : escapar siempre
$pseudoAffiche = htmlspecialchars($pseudo, ENT_QUOTES, 'UTF-8');
Independiente
Preparadas + htmlspecialchars + hash : el trío basta para lanzar sin falla grave.
PYME en Constantine
Riad añade los tokens CSRF a los pedidos antes de la apertura.
Equipo de producto
Imponer la check-list de los cuatro gestos en revisión de código sistemática.
Principiante
Esta semana : una visualización escapada y un registro hasheado.
✓ Buenas prácticas
- Pensar seguridad desde el diseño, en cada capa (entrada, SQL, HTML, acción, almacenamiento).
- Usar las herramientas probadas (preparadas, htmlspecialchars, password_hash, tokens) en lugar de casero.
- Formarse en continuo (OWASP Top 10) : los ataques evolucionan.
✗ Errores frecuentes
- Mostrar un dato de « su propia base » sin escapar (viene de usuarios).
- Olvidar el token CSRF en una acción sensible (pedido, eliminación).
- Mostrar los errores detallados en producción (información divulgada).
Puntos clave
- La SEGURIDAD es PERMANENTE, alrededor del principio : NUNCA CONFIAR en las entradas (todo lo exterior es sospechoso).
- INYECCIÓN SQL → PREPARADAS (nunca concatenación). XSS → ESCAPAR con htmlspecialchars(). CSRF → TOKENS + SameSite.
- Contraseñas HASHEADAS (password_hash/verify, NUNCA en claro ni en duro). HTTPS, SECRETOS fuera del código, errores ocultos en prod, AL DÍA.
- Herramientas PROBADAS + formación (OWASP Top 10). Un sitio no asegurado pone en peligro a sus usuarios — responsabilidad profesional.
Reto 48h — aplica mañana
Añadid 1 token CSRF verificado a un formulario, escapad 3 visualizaciones y convertid 1 almacenamiento en password_hash.
- Cito las cuatro defensas sin dudar.
- Ningún dato está concatenado ni mostrado en bruto.
- Ninguna contraseña existe en claro en mi código.
Preguntas frecuentes
XSS y CSRF : en concreto, ¿cómo protegerse?
El XSS : un atacante desliza JavaScript en un campo (comentario, apodo) mostrado sin escapar — se ejecuta en los visitantes (robo de sesión, redirección). Defensa : htmlspecialchars() en todo dato no fiable antes de mostrar (el script se vuelve texto inofensivo). El CSRF : una petición forjada desde otro sitio hace actuar a un conectado sin saberlo (cambiar un email, pedir). Defensa : token único y secreto por formulario, verificado al enviar (+ SameSite en las cookies). Dos mecanismos distintos, dos defensas sistemáticas.
¿Por dónde empezar en seguridad y qué mentalidad adoptar?
La mentalidad : « nunca confiar en las entradas » — todo lo exterior es sospechoso (incluso « vuestra » interfaz, sorteable en peticiones directas). Los fundamentos de fuerte impacto : (1) inyección SQL → preparadas, (2) XSS → escapar, (3) CSRF → tokens, (4) contraseñas → hash, (5) HTTPS, (6) secretos fuera del código, (7) errores ocultos en prod, (8) autorización (derechos, no solo conexión), (9) subidas bloqueadas. Luego OWASP Top 10 para estructurar la continuación.
Un visitante publica un apodo con JavaScript. ¿Reflejo correcto?