Volver al blog

Seguridad Web en 2025: OWASP Top 10 y Cómo Proteger Tus Aplicaciones JavaScript

Hola HaWkers, con ataques cibernéticos aumentando exponencialmente, seguridad dejó de ser responsabilidad apenas de especialistas. Todo desarrollador necesita conocer OWASP Top 10 e implementar protecciones básicas.

Vamos a explorar las vulnerabilidades más comunes y cómo proteger tus aplicaciones JavaScript.

OWASP Top 10: Las Vulnerabilidades Críticas

const owaspTop10_2025 = {
  1: 'Broken Access Control',
  2: 'Cryptographic Failures',
  3: 'Injection (SQL, XSS, etc)',
  4: 'Insecure Design',
  5: 'Security Misconfiguration',
  6: 'Vulnerable Components',
  7: 'Authentication Failures',
  8: 'Software and Data Integrity',
  9: 'Security Logging Failures',
  10: 'Server-Side Request Forgery (SSRF)'
};

1. Previniendo XSS (Cross-Site Scripting)

// ❌ VULNERABLE - ¡Nunca hagas esto!
function renderUserInput(input) {
  document.getElementById('output').innerHTML = input;
  // Si input = "<script>alert('XSS')</script>"
  // ¡Script ejecuta!
}

// ✅ SEGURO - Sanitiza input
import DOMPurify from 'dompurify';

function renderUserInputSafe(input) {
  const clean = DOMPurify.sanitize(input);
  document.getElementById('output').innerHTML = clean;
}

// ✅ AÚN MÁS SEGURO - Usa textContent
function renderText(input) {
  document.getElementById('output').textContent = input;
  // HTML no es interpretado
}

// React automáticamente escapa
function UserProfile({ username }) {
  return <div>{username}</div>; // ¡Safe!
}

2. SQL Injection Prevention

// ❌ VULNERABLE - String concatenation
async function getUser(userId) {
  const query = `SELECT * FROM users WHERE id = ${userId}`;
  // Si userId = "1 OR 1=1", ¡retorna TODOS usuarios!
  return await db.query(query);
}

// ✅ SEGURO - Prepared statements
async function getUserSafe(userId) {
  const query = 'SELECT * FROM users WHERE id = ?';
  return await db.query(query, [userId]);
}

// ✅ ORM aún más seguro
async function getUserORM(userId) {
  return await User.findById(userId);
  // ORM sanitiza automáticamente
}

3. Autenticación Segura

import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';

class AuthService {
  // Registrar usuario
  async register(email, password) {
    // Validar contraseña fuerte
    if (password.length < 12) {
      throw new Error('Contraseña debe tener mínimo 12 caracteres');
    }

    // Hash con bcrypt (¡NUNCA guardes contraseña plain text!)
    const saltRounds = 12;
    const hashedPassword = await bcrypt.hash(password, saltRounds);

    await db.users.create({
      email,
      password: hashedPassword
    });
  }

  // Login
  async login(email, password) {
    const user = await db.users.findOne({ email });

    if (!user) {
      // ✅ Mensaje genérico (no revela si email existe)
      throw new Error('Credenciales inválidas');
    }

    // Verificar contraseña
    const isValid = await bcrypt.compare(password, user.password);

    if (!isValid) {
      throw new Error('Credenciales inválidas');
    }

    // JWT con expiración corta
    const token = jwt.sign(
      { userId: user.id, email: user.email },
      process.env.JWT_SECRET,
      { expiresIn: '1h' }
    );

    return { token, user: { id: user.id, email: user.email } };
  }

  // Verificar token
  verifyToken(token) {
    try {
      return jwt.verify(token, process.env.JWT_SECRET);
    } catch {
      throw new Error('Token inválido');
    }
  }
}

4. CSRF Protection

// Backend - Express
import csrf from 'csurf';

const csrfProtection = csrf({ cookie: true });

app.post('/api/transfer', csrfProtection, async (req, res) => {
  // Token CSRF validado automáticamente
  await processTransfer(req.body);
  res.json({ success: true });
});

// Frontend - Enviar token
async function transferMoney(amount, to) {
  const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

  await fetch('/api/transfer', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': csrfToken
    },
    body: JSON.stringify({ amount, to })
  });
}

5. Validación de Input

import { z } from 'zod';

// Schema de validación
const userSchema = z.object({
  email: z.string().email(),
  age: z.number().min(18).max(120),
  username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/)
});

// Validar input
async function createUser(data) {
  try {
    // Valida y parsea
    const validated = userSchema.parse(data);

    // ✅ Datos garantidamente válidos
    return await db.users.create(validated);
  } catch (error) {
    throw new Error('Datos inválidos');
  }
}

6. Rate Limiting

import rateLimit from 'express-rate-limit';

// Prevenir brute force
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 5, // 5 intentos
  message: 'Muchos intentos de login. Intente en 15 minutos.'
});

app.post('/api/login', loginLimiter, async (req, res) => {
  // Login logic
});

// Rate limit general
const apiLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minuto
  max: 100 // 100 requests
});

app.use('/api/', apiLimiter);

7. Secure Headers

import helmet from 'helmet';

// Express - Helmet configura headers seguros
app.use(helmet());

// Headers importantes:
const securityHeaders = {
  'Content-Security-Policy': "default-src 'self'",
  'X-Frame-Options': 'DENY',
  'X-Content-Type-Options': 'nosniff',
  'Strict-Transport-Security': 'max-age=31536000',
  'X-XSS-Protection': '1; mode=block'
};

Checklist de Seguridad

const securityChecklist = [
  '✅ Validar TODO input del usuario',
  '✅ Usar prepared statements (SQL)',
  '✅ Sanitizar output HTML',
  '✅ Implementar rate limiting',
  '✅ HTTPS en producción (¡siempre!)',
  '✅ Passwords hasheadas con bcrypt',
  '✅ JWT con expiración corta',
  '✅ CSRF tokens en forms',
  '✅ Security headers (Helmet)',
  '✅ Dependencias actualizadas',
  '✅ Secrets en env vars',
  '✅ Logging de eventos de seguridad',
  '✅ CORS configurado correctamente'
];

Seguridad no es opcional en 2025. Con ataques cada vez más sofisticados, todo desarrollador es responsable por código seguro.

¡Vamos a por ello! 🦅

🎯 Desarrolladores Seguros Tienen Más Valor

Conocimiento de seguridad diferencia desarrolladores juniors de profesionales completos. Invierte en conocimiento sólido.

Comienza ahora:

  • $9.90 USD (pago único)

🚀 Acceder a la Guía Completa

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios