Prevención de XSS
A estas alturas ya sabemos:
-
Qué es XSS y sus tipos (Reflected, Stored, DOM, Blind)
-
Cómo detectarlo
-
Cómo explotarlo (phishing, defacing, session hijacking)
Ahora toca la parte defensiva: cómo prevenir XSS correctamente.
🧠 Idea clave
Una vulnerabilidad XSS siempre involucra dos elementos:
-
Source → entrada controlada por el usuario (formularios, headers, parámetros, etc.)
-
Sink → lugar donde esa entrada se muestra o se ejecuta (HTML, JS, DOM)
👉 Para prevenir XSS hay que proteger ambos, tanto en front-end como en back-end.
🖥️ Prevención en Front-end
El front-end es donde se recibe la mayoría de entradas del usuario, por lo que validar y sanear aquí reduce riesgos, pero nunca es suficiente por sí solo.
✅ Validación de entrada
La validación asegura que los datos tienen el formato esperado.
Ejemplo: validación de email con JavaScript
function validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
📌 Esto evita entradas inválidas pero no detiene XSS por sí solo.
🧼 Sanitización de entrada
La sanitización elimina o neutraliza código peligroso.
Una librería estándar es DOMPurify:
<script src="dist/purify.min.js"></script>
let clean = DOMPurify.sanitize(dirty);
📌 Esto elimina etiquetas <script>, eventos onerror, onclick, etc.
⛔ Entrada directa peligrosa
Nunca insertar directamente input del usuario en:
-
<script> -
<style> -
Atributos HTML
-
Comentarios HTML
Ejemplo peligroso:
<div name="INPUT_USUARIO"></div>
🚫 Funciones JavaScript peligrosas
Evitar usar input del usuario con:
-
innerHTML
-
outerHTML
-
document.write()
-
document.writeln()
-
document.domain
Y en jQuery:
-
html()
-
parseHTML()
-
append(), prepend()
-
before(), after()
-
replaceWith()
📌 Todas escriben HTML crudo, ideal para XSS.
🗄️ Prevención en Back-end
El back-end es la defensa real. Todo lo del front-end puede ser bypassed.
✅ Validación de entrada en Back-end
Ejemplo en PHP:
if (filter_var($_GET['email'], FILTER_VALIDATE_EMAIL)) {
// procesar
} else {
// rechazar entrada
}
📌 Si no cumple el formato esperado → no se procesa ni se muestra.
🧼 Sanitización de entrada en Back-end
Nunca mostrar input del usuario sin sanear.
Ejemplo en PHP:
addslashes($_GET['email']);
Ejemplo en NodeJS con DOMPurify:
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirty);
🔐 Codificación de salida (Output Encoding)
Convierte caracteres peligrosos en entidades HTML.
Ejemplo PHP:
htmlentities($_GET['email']);
Ejemplo NodeJS:
import encode from 'html-entities';
encode('<'); // <
📌 El navegador muestra el texto sin ejecutarlo.
⚙️ Configuración del servidor
Medidas adicionales muy importantes:
-
Usar HTTPS siempre
-
X-Content-Type-Options: nosniff
-
Content-Security-Policy (CSP)
- Ejemplo: script-src 'self'
-
Cookies con flags:
-
HttpOnly
-
Secure
-
-
Web Application Firewall (WAF)
📌 HttpOnly evita session hijacking por XSS.
🧱 Protección por frameworks
Muchos frameworks incluyen protección XSS:
-
Angular
-
React (escapa por defecto)
⚠️ Aun así, pueden existir fallos de lógica.
🧠 Conclusión
No existe una única defensa mágica contra XSS.
La protección real se basa en:
-
Validación estricta
-
Sanitización
-
Output encoding
-
Configuración segura del servidor
-
Defensa en capas
👉 Incluso con todo esto, siempre hay que pensar como atacante.
La mejor defensa es conocer bien el ataque.
🧪 Mentalidad de seguridad
Practicar:
-
XSS ofensivo
-
XSS defensivo
Es la única forma de alcanzar un nivel realmente sólido frente a XSS.