OWASP Top 10 para APIs

Una guía completa sobre las vulnerabilidades más críticas en APIs y cómo mitigarlas

¿Qué es OWASP?

El Open Web Application Security Project (OWASP) es una fundación sin fines de lucro que trabaja para mejorar la seguridad del software. Su lista Top 10 identifica los riesgos de seguridad más críticos para las aplicaciones web y APIs y similares.

¿Por qué es importante la seguridad en APIs?

Las APIs son la columna vertebral de las aplicaciones modernas. Según estudios recientes, el 91% de las organizaciones sufrieron incidentes de seguridad relacionados con APIs en 2022, y el tráfico malicioso hacia APIs aumentó un 681% en el último año.

Los 10 Riesgos Principales

1

Broken Object Level Authorization (BOLA)

Vector de ataque

Modificación de identificadores en la URL o cuerpo para acceder a objetos ajenos.

Debilidad

Falta de verificación de permisos a nivel de objeto.

Impacto

Acceso no autorizado a datos de otros usuarios.

Estrategias de mitigación

  • Validar siempre que el usuario autenticado tenga acceso al recurso.
  • Implementar lógica de autorización robusta basada en el usuario.
  • Evitar confiar en los identificadores enviados por el cliente.

Ejemplo en Express

// Código vulnerable

// API permite acceder a cualquier recurso por ID sin verificación
app.get('/api/users/:id', async (req, res) => {
  const user = await db.findUserById(req.params.id);
  res.json(user);
});

// Código seguro

app.get('/api/users/:id', async (req, res) => {
  const requestedId = req.params.id;
  const authenticatedId = req.user.id; // obtenido del token/session

  if (requestedId !== authenticatedId) {
    return res.status(403).json({ message: 'Unauthorized access' });
  }

  const user = await db.findUserById(requestedId);
  res.json(user);
});

// Código vulnerable

app.put('/api/orders/:orderId', async (req, res) => {
  const order = await db.findOrderById(req.params.orderId);
  order.status = req.body.status;
  await db.save(order);
  res.send("Order updated");
});

// Código seguro

app.put('/api/orders/:orderId', async (req, res) => {
  const order = await db.findOrderById(req.params.orderId);

  if (order.userId !== req.user.id) {
    return res.status(403).send("Unauthorized to modify this order");
  }

  order.status = req.body.status;
  await db.save(order);
  res.send("Order updated");
});

// Código vulnerable

// Devuelve detalles de todos los reportes relacionados a un proyecto
app.get('/api/projects/:projectId/reports', async (req, res) => {
  const reports = await db.getReportsByProject(req.params.projectId);
  res.json(reports);
});

// Código seguro

app.get('/api/projects/:projectId/reports', async (req, res) => {
  const project = await db.getProjectById(req.params.projectId);

  if (!project || project.ownerId !== req.user.id) {
    return res.status(403).send("Access denied");
  }

  const reports = await db.getReportsByProject(project.id);
  res.json(reports);
});
2

Broken Authentication

Vector de ataque

Reutilización de credenciales, tokens mal gestionados, sesiones inseguras.

Debilidad

Implementación débil del sistema de autenticación.

Impacto

Suplantación de identidad y acceso completo a la cuenta de un usuario.

Estrategias de mitigación

  • Utilizar bibliotecas seguras como Passport.js.
  • Aplicar expiración de tokens y rotación periódica.
  • Usar autenticación multifactor (2FA).

Ejemplo en Express

// Código vulnerable

app.post('/login', async (req, res) => {
  const { username } = req.body;
  const user = await db.findUserByUsername(username);

  if (!user) return res.status(401).send("Invalid user");
  
  // ❌ No hay validación de contraseña
  req.session.userId = user.id;
  res.send("Logged in");
});

// Código seguro

const bcrypt = require('bcrypt');

app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await db.findUserByUsername(username);

  if (!user || !await bcrypt.compare(password, user.passwordHash)) {
    return res.status(401).send("Invalid credentials");
  }

  req.session.userId = user.id;
  res.send("Logged in");
});

// Código vulnerable

// Login
app.post('/login', async (req, res) => {
  const user = await db.findUserByUsername(req.body.username);
  req.session.userId = user.id;
  res.send("Logged in");
});

// Logout
app.post('/logout', (req, res) => {
  // ❌ No se destruye la sesión
  res.send("Logged out");
});

// Código seguro

// Login con sesión segura y duración limitada
app.post('/login', async (req, res) => {
  const user = await db.findUserByUsername(req.body.username);
  if (!user) return res.status(401).send("Unauthorized");

  req.session.regenerate((err) => {
    if (err) return res.status(500).send("Error");
    req.session.userId = user.id;
    req.session.cookie.maxAge = 15 * 60 * 1000; // 15 minutos
    res.send("Logged in");
  });
});

// Logout
app.post('/logout', (req, res) => {
  req.session.destroy(err => {
    if (err) return res.status(500).send("Logout failed");
    res.clearCookie('connect.sid');
    res.send("Logged out");
  });
});

// Código vulnerable

const jwt = require('jsonwebtoken');

app.get('/profile', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  const payload = jwt.decode(token); // ❌ decode sin verificación
  res.send(`Welcome ${payload.username}`);
});

// Código seguro

const jwt = require('jsonwebtoken');

app.get('/profile', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET);
    res.send(`Welcome ${payload.username}`);
  } catch (err) {
    res.status(401).send("Invalid or expired token");
  }
});
3

Broken Object Property Level Authorization

Vector de ataque

Manipulación del payload para modificar propiedades no autorizadas.

Debilidad

No se controla qué campos pueden ser escritos por el cliente.

Impacto

Escalada de privilegios o alteración de información crítica.

Estrategias de mitigación

  • Validar y filtrar los campos permitidos (whitelisting).
  • Descartar cualquier propiedad no esperada en el servidor.

Ejemplo en Express

// Código vulnerable

app.put('/api/users/:id', async (req, res) => {
  const updatedUser = await db.updateUser(req.params.id, req.body);
  res.json(updatedUser);
});

//El usuario puede enviar un cuerpo como: { "email": "nuevo@mail.com", "role": "admin" }

// Código seguro

app.put('/api/users/:id', async (req, res) => {
  const updates = { email: req.body.email }; // solo campos permitidos

  if (req.body.role) {
    // No permitir cambios de rol por usuarios normales
    return res.status(403).send("Cannot change role");
  }

  const updatedUser = await db.updateUser(req.params.id, updates);
  res.json(updatedUser);
});

// Código vulnerable

app.put('/api/orders/:id', async (req, res) => {
  const order = await db.findOrderById(req.params.id);
  Object.assign(order, req.body);
  await db.save(order);
  res.json(order);
});

//Un cliente podría cambiar el estado de su pedido a "shipped" o "paid".

// Código seguro

app.put('/api/orders/:id', async (req, res) => {
  const order = await db.findOrderById(req.params.id);

  // Solo permitir cambiar dirección si es el propietario
  if (order.userId !== req.user.id) {
    return res.status(403).send("Unauthorized");
  }

  const allowedUpdates = {
    shippingAddress: req.body.shippingAddress
  };

  Object.assign(order, allowedUpdates);
  await db.save(order);
  res.json(order);
});

// Código vulnerable

// Actualiza configuración del usuario (incluye propiedades anidadas)
app.patch('/api/users/:id/settings', async (req, res) => {
  const user = await db.findUserById(req.params.id);
  Object.assign(user.settings, req.body);
  await db.save(user);
  res.send("Settings updated");
});

//El usuario podría enviar: { "2FAEnabled": false, "adminPanelAccess": true }

// Código seguro

app.patch('/api/users/:id/settings', async (req, res) => {
  const user = await db.findUserById(req.params.id);

  if (user.id !== req.user.id) {
    return res.status(403).send("Forbidden");
  }

  // Definir propiedades seguras a modificar
  const allowed = ["theme", "language", "notifications"];
  for (const key of Object.keys(req.body)) {
    if (!allowed.includes(key)) {
      return res.status(400).send(`Property '${key}' not allowed`);
    }
    user.settings[key] = req.body[key];
  }

  await db.save(user);
  res.send("Settings updated");
});
4

Unrestricted Resource Consumption

Vector de ataque

Peticiones masivas o envío de datos muy grandes.

Debilidad

Falta de límites en uso de CPU, RAM, ancho de banda, etc.

Impacto

Denegación de servicio (DoS) o agotamiento de recursos.

Estrategias de mitigación

  • Aplicar rate limiting y control de tamaño en payloads.
  • Validar tamaños máximos en cargas y consultas.

Ejemplo en Express

// Código vulnerable

app.get('/download', (req, res) => {
  const filePath = path.join(__dirname, 'uploads', req.query.file);
  res.download(filePath);
});

// Código seguro

const MAX_FILE_SIZE_MB = 50;

app.get('/download', (req, res) => {
  const filePath = path.join(__dirname, 'uploads', req.query.file);

  fs.stat(filePath, (err, stats) => {
    if (err || stats.size > MAX_FILE_SIZE_MB * 1024 * 1024) {
      return res.status(400).send('File too large or not found');
    }

    res.download(filePath);
  });
});

// Código vulnerable

app.get('/api/products', async (req, res) => {
  const products = await db.getAllProducts(); // puede ser miles
  res.json(products);
});

// Código seguro

app.get('/api/products', async (req, res) => {
  const page = parseInt(req.query.page || '1');
  const limit = Math.min(parseInt(req.query.limit || '10'), 100); // máximo 100

  const products = await db.getProducts({ page, limit });
  res.json(products);
});

// Código vulnerable

app.post('/generate-pdf', async (req, res) => {
  const { htmlContent } = req.body;

  const pdfBuffer = await generatePDF(htmlContent); // sin límite
  res.setHeader('Content-Type', 'application/pdf');
  res.send(pdfBuffer);
});

// Código seguro

app.post('/generate-pdf', async (req, res) => {
  const { htmlContent } = req.body;

  if (!htmlContent || htmlContent.length > 50000) {
    return res.status(400).send("Content too large");
  }

  try {
    const pdfBuffer = await generatePDF(htmlContent);
    res.setHeader('Content-Type', 'application/pdf');
    res.send(pdfBuffer);
  } catch (e) {
    res.status(500).send("Error generating PDF");
  }
});
5

Broken Function Level Authorization

Vector de ataque

Acceso a funciones restringidas sin verificar permisos de rol.

Debilidad

No se verifica el nivel de privilegio requerido por función.

Impacto

Usuarios no autorizados pueden realizar acciones administrativas.

Estrategias de mitigación

  • Verificar explícitamente los roles/autorizaciones por función.
  • Seguir el principio de mínimo privilegio.

Ejemplo en Express

// Código vulnerable

app.delete('/admin/users/:id', async (req, res) => {
  await db.deleteUser(req.params.id);
  res.send("User deleted");
});

// Código seguro

app.delete('/admin/users/:id', async (req, res) => {
  if (req.user.role !== 'admin') {
    return res.status(403).send("Forbidden");
  }

  await db.deleteUser(req.params.id);
  res.send("User deleted");
});

// Código vulnerable

app.get('/reports/monthly', async (req, res) => {
  const report = await db.getMonthlyFinancialReport();
  res.json(report);
});

// Código seguro

app.get('/reports/monthly', async (req, res) => {
  if (!req.user.permissions.includes('VIEW_FINANCIAL_REPORTS')) {
    return res.status(403).send("Access denied");
  }

  const report = await db.getMonthlyFinancialReport();
  res.json(report);
});

// Código vulnerable

// Endpoint de sistema pensado para tareas internas
app.post('/internal/reset-system', async (req, res) => {
  await system.resetAll();
  res.send("System reset");
});

// Código seguro

app.post('/internal/reset-system', async (req, res) => {
  if (!req.user || req.user.role !== 'sysadmin') {
    return res.status(403).send("Unauthorized");
  }

  await system.resetAll();
  res.send("System reset");
});
6

Unrestricted Access to Sensitive Business Flows

Vector de ataque

Automatización de flujos como compra rápida, scraping o abusos lógicos.

Debilidad

Falta de validaciones contra abuso de lógica del negocio.

Impacto

Impacto económico, saturación del sistema o fraude.

Estrategias de mitigación

  • Monitorear patrones de uso anómalos.
  • Limitar acciones repetidas por usuario/IP.
  • Aplicar CAPTCHA u otros mecanismos de fricción.

Ejemplo en Express

// Código vulnerable

app.post('/generate-coupon', (req, res) => {
  const coupon = generateCoupon();
  db.saveCoupon(req.user.id, coupon);
  res.json({ coupon });
});

// Código seguro

const rateLimit = require("express-rate-limit");

const couponLimiter = rateLimit({
  windowMs: 10 * 60 * 1000, // 10 minutos
  max: 1, // 1 intento cada 10 minutos por IP
  message: "Too many coupon requests. Try later.",
});

app.post('/generate-coupon', couponLimiter, (req, res) => {
  const coupon = generateCoupon();
  db.saveCoupon(req.user.id, coupon);
  res.json({ coupon });
});

// Código vulnerable

app.post('/register', async (req, res) => {
  await db.createUser(req.body);
  res.send("User created");
});

// Código seguro

const captchaValidator = require('./captchaValidator');

app.post('/register', async (req, res) => {
  const validCaptcha = await captchaValidator.verify(req.body.captchaToken);
  if (!validCaptcha) return res.status(400).send("Invalid CAPTCHA");

  await db.createUser(req.body);
  res.send("User created");
});

// Código vulnerable

app.post('/transfer', async (req, res) => {
  await transferFunds(req.user.id, req.body.to, req.body.amount);
  res.send("Transfer completed");
});

// Código seguro

app.post('/transfer', async (req, res) => {
  const recentTransfers = await db.countUserTransfers(req.user.id, { withinMinutes: 10 });

  if (recentTransfers >= 3) {
    logAnomaly(req.user.id, 'Too many transfers');
    return res.status(429).send("Too many transfer attempts");
  }

  await transferFunds(req.user.id, req.body.to, req.body.amount);
  res.send("Transfer completed");
});
7

Server-Side Request Forgery (SSRF)

Vector de ataque

Manipulación del servidor para que realice solicitudes HTTP a destinos arbitrarios.

Debilidad

Falta de validación de URLs proporcionadas por el usuario antes de que el servidor realice solicitudes.

Impacto

Escaneo de puertos internos, acceso a servicios internos, exfiltración de datos o ejecución de código remoto.

Estrategias de mitigación

  • Sanitizar y validar todas las entradas de datos del cliente.
  • Implementar listas blancas de dominios/IPs permitidos.
  • Bloquear tráfico a direcciones IP privadas y localhost.
  • Implementar políticas de firewall para bloquear conexiones no esenciales.
  • No enviar respuestas en bruto al cliente.

Ejemplo de mitigación de SSRF

// Código vulnerable

// El usuario puede hacer que el servidor haga peticiones arbitrarias
app.post('/fetch', async (req, res) => {
  const { url } = req.body;
  const response = await fetch(url); // ❌ SSRF posible
  const data = await response.text();
  res.send(data);
});

// Código seguro

const allowedDomains = ['https://api.example.com', 'https://docs.example.com'];
          
app.post('/fetch', async (req, res) => {
  const { url } = req.body;

  if (!allowedDomains.some(domain => url.startsWith(domain))) {
    return res.status(403).send("URL not allowed");
  }

  const response = await fetch(url); // ✅ acceso limitado
  const data = await response.text();
  res.send(data);
});

// Código vulnerable

// Permite acceder a URLs internas como http://localhost/admin o 169.254.x.x
app.get('/image-proxy', async (req, res) => {
  const { imageUrl } = req.query;
  const response = await fetch(imageUrl);
  const buffer = await response.arrayBuffer();
  res.setHeader('Content-Type', 'image/jpeg');
  res.send(Buffer.from(buffer));
});

// Código seguro

const dns = require('dns').promises;
const { URL } = require('url');

async function isPrivateIp(urlString) {
  try {
    const url = new URL(urlString);
    const addresses = await dns.lookup(url.hostname, { all: true });

    return addresses.some(addr => {
      return /^127\./.test(addr.address) || // localhost
             /^10\./.test(addr.address) ||  // red privada
             /^192\.168\./.test(addr.address) ||
             /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(addr.address);
    });
  } catch (e) {
    return true; // fallback a denegar
  }
}

app.get('/image-proxy', async (req, res) => {
  const { imageUrl } = req.query;

  if (await isPrivateIp(imageUrl)) {
    return res.status(403).send("Access to internal resources denied");
  }

  const response = await fetch(imageUrl);
  const buffer = await response.arrayBuffer();
  res.setHeader('Content-Type', 'image/jpeg');
  res.send(Buffer.from(buffer));
});

// Código vulnerable

// Recibe una URL de webhook sin validación
app.post('/register-webhook', async (req, res) => {
  const { url } = req.body;
  db.saveWebhook(url); // ❌ el servidor luego enviará datos a cualquier URL
  res.send("Webhook saved");
});

// Código seguro

const validWebhookDomain = /^https:\/\/hooks\.example\.com\/.*/;
          
app.post('/register-webhook', async (req, res) => {
  const { url } = req.body;

  if (!validWebhookDomain.test(url)) {
    return res.status(400).send("Invalid webhook URL");
  }

  db.saveWebhook(url); // ✅ solo dominios específicos permitidos
  res.send("Webhook saved");
});
8

Security Misconfiguration

Vector de ataque

Explotación de sistemas mal configurados, incompletos o con configuraciones por defecto.

Debilidad

Configuraciones de seguridad incorrectas, servicios innecesarios habilitados, permisos incorrectos, etc.

Impacto

Acceso no autorizado a datos o funcionalidades del sistema, hasta compromiso completo del servidor.

Estrategias de mitigación

  • Implementar un proceso de endurecimiento de seguridad para todos los entornos.
  • Eliminar o no instalar características, componentes o servicios no utilizados.
  • Revisar y actualizar configuraciones como parte del proceso de gestión de parches.
  • Implementar una arquitectura de segmentación con separación entre componentes.
  • Enviar directivas de seguridad a los clientes (headers de seguridad).

Ejemplo de configuración segura

// Código vulnerable

// Express sin ningún header de seguridad
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send("Hello world");
});

app.listen(3000);

// Código seguro

// Express usando helmet para agregar headers seguros
const express = require('express');
const helmet = require('helmet');

const app = express();
app.use(helmet()); // ✅ Activa varios headers de seguridad

app.get('/', (req, res) => {
  res.send("Hello world");
});

app.listen(3000);

// Código vulnerable

// Express con stack traces en producción
app.use((err, req, res, next) => {
  // ❌ Devuelve detalles del error al cliente
  res.status(500).send(err.stack);
});

// Código seguro

// Manejo de errores controlado
app.use((err, req, res, next) => {
  // ✅ No revela información sensible en producción
  console.error(err); // log interno
  res.status(500).send("Something went wrong");
});

// Código vulnerable

// Rutas administrativas sin protección
app.use('/admin', require('./adminRoutes'));

// adminRoutes.js
router.get('/dashboard', (req, res) => {
  res.send("Admin Panel");
});

// ⚠️ Si un atacante descubre /admin, puede acceder libremente.

// Código seguro

// Middleware de autenticación para rutas de admin
function isAdmin(req, res, next) {
  if (req.user && req.user.role === 'admin') return next();
  return res.status(403).send("Forbidden");
}

app.use('/admin', isAdmin, require('./adminRoutes'));
9

Improper Inventory Management

Vector de ataque

Acceso a APIs antiguas, no documentadas o desactivadas.

Debilidad

Falta de visibilidad del ciclo de vida de las APIs.

Impacto

Exposición de endpoints obsoletos y vulnerables.

Estrategias de mitigación

  • Mantener inventario y control de versiones.
  • Aplicar procesos de ciclo de vida (API lifecycle management).

Ejemplo en Express

// Código vulnerable

// ⚠️ Este endpoint fue usado para pruebas internas y quedó accesible en producción
app.get('/test-delete-all-users', async (req, res) => {
  await db.deleteAllUsers();
  res.send('All users deleted');
});

// Código seguro

// ✅ Eliminar endpoints no documentados y que no pertenecen al flujo oficial de la aplicación

// Esta ruta se elimina completamente del código antes del despliegue
// Alternativamente, puede protegerse con autorización y limitarse al entorno de staging

// Código vulnerable

// ⚠️ Ruta antigua que sigue funcionando sin autenticación ni control
app.get('/api/v1/user-data', (req, res) => {
  res.send(db.getUserById(req.query.id));
});

// Código seguro

// ✅ Control de versiones y desactivación explícita de APIs antiguas
app.use('/api/v1/*', (req, res) => {
  res.status(410).send("Esta versión de la API está obsoleta");
});

// Solo permitir acceso a /api/v2 con autenticación y control adecuado
app.get('/api/v2/user-data', authenticate, (req, res) => {
  res.send(db.getUserById(req.user.id));
});

// Código vulnerable

// ⚠️ Ruta de administración visible por convención
app.get('/admin/feature-flags', (req, res) => {
  res.json(db.getFeatureFlags());
});

// Código seguro

const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const app = express();

app.use(helmet()); // 🛡️ Protege cabeceras comunes

// ✅ Middleware de autenticación de administradores
function authenticateAdmin(req, res, next) {
  const user = req.user; // Suponiendo que viene desde JWT o sesión

  if (!user || !user.roles.includes('admin')) {
    return res.status(403).send('Acceso denegado');
  }

  next();
}

// ✅ Usar rutas menos triviales y fuera de patrones comunes
app.get('/internal/v2/settings/flags', authenticateAdmin, async (req, res) => {
  try {
    const flags = await db.getFeatureFlags();

    res.json({
      flags,
      accessedAt: new Date().toISOString()
    });
  } catch (error) {
    res.status(500).send('Error al recuperar los flags');
  }
});
10

Unsafe Consumption of APIs

Vector de ataque

Consumo de APIs externas sin validar sus datos.

Debilidad

Confianza ciega en terceros sin validación de entrada.

Impacto

Inyección de datos maliciosos o ejecución de código no seguro.

Estrategias de mitigación

  • Validar siempre la respuesta de servicios externos.
  • Aplicar filtrado y sanitización antes de usar datos de terceros.

Ejemplo en Express

// Código vulnerable

const axios = require('axios');

app.get('/api/currency-exchange', async (req, res) => {
  const { from, to } = req.query;

  // ⚠️ No validamos la respuesta del servicio externo, lo que puede llevar a datos no deseados o incorrectos
  const result = await axios.get(`https://external-api.com/exchange?from=${from}&to=${to}`);

  res.send(result.data);  // Se asume que la respuesta es siempre correcta
});

// Código seguro

const axios = require('axios');

// Función para validar la respuesta de la API externa
function isValidExchangeRateResponse(data) {
  return data && typeof data.rate === 'number';  // Validación simple de que existe la propiedad 'rate' y es un número
}

app.get('/api/currency-exchange', async (req, res) => {
  const { from, to } = req.query;

  try {
    const result = await axios.get(`https://external-api.com/exchange?from=${from}&to=${to}`);

    // ✅ Validar la respuesta antes de usarla
    if (!isValidExchangeRateResponse(result.data)) {
      return res.status(500).send('Error en la respuesta de la API externa');
    }

    res.send(result.data);
  } catch (error) {
    res.status(500).send('Error al obtener datos de cambio de divisas');
  }
});

// Código vulnerable

app.get('/api/data', async (req, res) => {
  const { userId } = req.query;

  // ⚠️ Llamada a API externa sin ningún tipo de autenticación o verificación
  const response = await axios.get(`https://external-api.com/data/${userId}`);

  res.send(response.data);
});

// Código seguro

app.get('/api/data', authenticateUser, async (req, res) => {
  const { userId } = req.query;

  // ✅ Verificar que el usuario autenticado tenga acceso a los datos solicitados
  if (req.user.id !== userId) {
    return res.status(403).send('Acceso no autorizado');
  }

  try {
    const response = await axios.get(`https://external-api.com/data/${userId}`, {
      headers: { Authorization: `Bearer ${req.user.token}` } // Usar autenticación de API
    });
    res.send(response.data);
  } catch (error) {
    res.status(500).send('Error al recuperar los datos');
  }
});

// Código vulnerable

const axios = require('axios');

app.get('/api/product-info', async (req, res) => {
  const { productId } = req.query;

  // ⚠️ Llamada a API externa sin validación ni filtrado de la respuesta
  const response = await axios.get(`https://external-api.com/product/${productId}`);

  // La respuesta es directamente pasada sin ningún control
  res.send(response.data);
});

// Código seguro

const axios = require('axios');
const { body, validationResult } = require('express-validator');  // Usando express-validator para la validación

// Función para validar la respuesta de un producto
function isValidProductResponse(data) {
  return data && data.id && data.name && typeof data.price === 'number';  // Asegurarse de que la respuesta tiene todos los campos necesarios
}

app.get('/api/product-info', [
  body('productId').isInt().withMessage('El productId debe ser un número entero'),  // Validar productId
], async (req, res) => {
  // Validar la entrada del usuario
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  const { productId } = req.query;

  try {
    const response = await axios.get(`https://external-api.com/product/${productId}`);

    // ✅ Validación de la respuesta
    if (!isValidProductResponse(response.data)) {
      return res.status(500).send('Datos inválidos recibidos de la API');
    }

    // ✅ Filtrado de la respuesta para evitar posibles scripts maliciosos
    const sanitizedProductData = {
      ...response.data,
      name: xss(response.data.name),  // Sanitizar el nombre del producto
      description: xss(response.data.description),  // Sanitizar la descripción
    };

    res.send(sanitizedProductData);
  } catch (error) {
    res.status(500).send('Error al obtener los datos del producto');
  }
});

Estadísticas de Seguridad en APIs

91%

de las organizaciones sufrieron incidentes de seguridad relacionados con APIs en 2022

681%

de aumento en el tráfico malicioso hacia APIs en el último año

42%

de las organizaciones no tienen una estrategia de seguridad para APIs