3.4 · Seguridad : inyección SQL, XSS, CSRF, hash

Nivel 3 · Bases de datos & POO

3.4Seguridad : inyección SQL, XSS, CSRF, hash

Objetivo: proteger una aplicación contra las fallas mayores (inyección SQL, XSS, CSRF), hashear las contraseñas y aplicar las bases (HTTPS, secretos, errores) — competencia fundamental del desarrollador web.
Tiempo estimado: 13 min · lectura autónoma · Si solo tienes 5 min: lee «Lo esencial en 30s» + el esquema.

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.

Error frecuente de interpretación: « Mostrar con echo un dato de mi propia base es seguro, y hashear ralentiza la conexión ». No. La base contiene datos introducidos por usuarios — luego no fiables por construcción, para escapar en cada visualización. Y password_verify() tarda unos milisegundos : un coste invisible para el usuario, pero que vuelve inexplotable el robo de base. La seguridad no se negocia por comodidad.
El cuarteto de blindaje Cuatro puertas, cuatro cerrojos Entradas sospechosas Validarprimero, siempreSQL + HTML Preparadas +htmlspecialcharsFormularios Token CSRFverificado + SameSiteCuentas password_hash /verify, HTTPS, secretos
Cuatro puertas, cuatro cerrojos
Versión en texto del esquema (SEO / lectores de pantalla / traducción): 1) Entradas : Riad trata cada dato exterior como sospechoso y lo valida. 2) Salidas SQL y HTML : consultas preparadas hacia la base, htmlspecialchars() hacia el navegador. 3) Acciones : cada formulario lleva un token CSRF verificado en sesión. 4) Cuentas : contraseñas hasheadas, HTTPS, secretos en entorno. Lección : pensar seguridad desde el diseño, en cada capa.

🔎 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

  1. Contra la inyección SQL : SIEMPRE consultas preparadas (PDO) — verificad que ningún dato esté concatenado en el SQL.
  2. Contra el XSS : escapad todo dato mostrado con htmlspecialchars() (comentarios, apodos, datos de base).
  3. Contra el CSRF : añadid un token CSRF (único, secreto) en vuestros formularios y verificadlo al enviar ; usad SameSite en las cookies.
  4. 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.
Protegéis vuestra aplicación contra las fallas mayores (inyección SQL, XSS, CSRF), hasheáis las contraseñas y aplicáis las bases de seguridad (HTTPS, secretos, errores) — una competencia fundamental del desarrollador web.

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.

Prudencia: Seguridad — reflejos vitales : no almacenéis NUNCA una contraseña en claro o en duro (ni en base, ni en el código, ni en las capturas) : password_hash() al almacenar, password_verify() al conectar. No concatenéis NUNCA un dato en SQL (consultas preparadas) y no mostréis NUNCA un dato no fiable sin htmlspecialchars(). Trabajad con cuentas y datos de prueba, mantened secretos y claves fuera del repositorio (variables de entorno), y en caso de exposición (contraseña o clave divulgada), consideradla comprometida : regeneradla inmediatamente.

✓ 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.

Lista de salida de la sección
  • 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.
Autoevaluación 1-5 : ¿tiene cada salida (SQL, HTML, acción, almacenamiento) su defensa?¿encontraría una auditoría un secreto en claro?

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.

Comprueba tu comprensión

Un visitante publica un apodo con JavaScript. ¿Reflejo correcto?

Recursos adicionales (opcional, para ir más allá)

Examen del nivel 3Preguntas inéditas y más difíciles para validar tus conocimientos