Software Testing & QA Services

Protegiendo APIs y aplicaciones backend: errores comunes y soluciones modernas

Tags: Tecnologías
apis backend

 

La mayoría de incidentes en APIs no ocurren por criptografía rota, sino por fallos de diseño y controles de autorización débiles (p. ej., Broken Object Level Authorization, Broken Authentication, mala validación de entrada, rate limiting ausente).

 

La base es: autenticación y autorización correctas, validación estricta, menor exposición posible, telemetría útil y transporte seguro.

 

OWASP API Security Top 10 (ed. 2023) y OWASP ASVS son referencias normativas que conviene usar como checklist de entrega y auditoría.

 

Quick Threat Modeling

 

El modelo de amenazas rápido (a veces llamado quick threat modeling) es una versión ligera y ágil del proceso formal de threat modeling, pensado para evaluar riesgos de seguridad sin frenar el desarrollo.

 

En lugar de hacer un análisis exhaustivo (que puede tardar semanas), se busca identificar de forma rápida los vectores de ataque más probables y críticos, para aplicar defensas lo antes posible.

 

Objetivos:

 

  • Detectar los puntos más vulnerables de una API o backend antes de que se ponga en producción.
  • Priorizar mitigaciones que tengan mayor impacto en la reducción de riesgos.
  • Evitar la falsa sensación de seguridad de “lo revisamos al final”.

 

Proceso

 

Definir el alcance mínimo

 

  • ¿Qué componente, API o microservicio evaluaremos?
  • ¿Qué datos maneja? (públicos, internos, confidenciales)

 

Identificar activos críticos

 

Ejemplo: credenciales, datos de usuarios, tokens, configuración interna.

 

Detectar amenazas obvias usando mnemotecnias como STRIDE o OWASP Top 10

 

  • Spoofing (suplantación)
  • Tampering (alteración de datos)
  • Repudiation (no poder probar acciones)
  • Information disclosure (exposición de información)
  • Denial of service (denegación de servicio)
  • Elevation of privilege (escalar privilegios)

 

Evaluar probabilidad e impacto

 

Alto / Medio / Bajo.

 

Definir mitigaciones rápidas

 

Autenticación más fuerte, sanitización de datos, rate limiting, logging, etc.

 

apis backend

 

Errores comunes (y cómo reconocerlos)

 

Broken Object Level Authorization (BOLA/IDOR)

Ocurre cuando la API permite acceder o modificar un recurso únicamente conociendo su identificador, sin verificar que el usuario tenga permiso para hacerlo.

Síntoma: /users/123 accesible por cualquiera que no sea el dueño.

Detección: pruebas con usuarios distintos, fuzzing de IDs, revisiones de política ABAC/RBAC. OWASP Foundation

 

Broken Authentication

Broken Authentication es una categoría de vulnerabilidad que ocurre cuando los mecanismos de autenticación de una API o backend están mal diseñados, implementados o configurados, permitiendo que un atacante suplante la identidad de otro usuario o incluso obtenga acceso total al sistema.

 

Ejemplos comunes:

 

  • Tokens JWT sin expiración o con clave insegura
  • Contraseñas débiles y sin validación
  • Falta de bloqueo por intentos fallidos
  • Exposición de credenciales en URLs

Detección: revisión de flujos OAuth 2.0, duración y audience de tokens, bloqueo por intentos. datatracker.ietf.org

 

Exposición excesiva de datos / Mass assignment

Ocurre cuando la API devuelve más información de la necesaria para el caso de uso del cliente, confiando en que “el frontend filtrará lo que no se usa”. Atacantes pueden usar la información “extra” para ingeniería social.

Detección: contract testing, schema diff entre entidades y DTOs, revisiones automáticas.

 

Falta de rate limiting y quotas

Sucede cuando una API no restringe cuántas solicitudes puede hacer un cliente en un periodo de tiempo o no impone cuotas de uso por usuario, aplicación o clave API.

 

Esto permite a un atacante probar credenciales en masa (ataque de fuerza bruta a /login), extraer grandes volúmenes de datos sin restricción o saturar la infraestructura (API DoS).

 

API sin umbrales ni respuesta 429 definida; sin circuit breakers facilita abusos de recursos y costos. rfc-editor.org

 

Validación pobre de entrada / salida

Esta falla permite que datos maliciosos entren al sistema o que información sensible salga accidentalmente, facilitando ataques como inyección SQL, XSS, desbordamiento de búfer o exposición de datos sensibles.

 

En la entrada de datos están los casos en que la aplicación acepta datos de usuario sin verificar tipo, longitud, formato o contenido puede llevar a inyecciones, corrupción de datos o ejecución de código.

 

En la salida los datos enviados al cliente no son filtrados ni sanitizados y puede exponer información sensible o permitir ataques de reflected XSS en frontends.

 

Detección: validación declarativa y conformance tests.

 

CORS mal configurado

La mala configuración de CORS (Cross-Origin Resource Sharing) es un problema común en APIs y backends que puede permitir que sitios web no autorizados interactúen con tu API, exponiendo datos o funcionalidades sensibles.

 

CORS es un mecanismo de seguridad en navegadores que restringe solicitudes HTTP hechas desde un origen distinto al de la API (dominio, protocolo o puerto distinto).

 

Un origen autorizado puede consumir la API.
Un origen no autorizado es bloqueado por el navegador.
El problema aparece cuando la API maneja estas restricciones de manera incorrecta, permitiendo accesos de cualquier origen (*) o confiando en encabezados inseguros.
Access-Control-Allow-Origin: * con credenciales, o permitir métodos sensibles sin revisión.

 

Transporte inseguro

Ocurre cuando los datos entre el cliente y la API viajan sin cifrado o con cifrado débil, exponiéndolos a interceptación, manipulación o robo. Esto afecta tanto la confidencialidad como la integridad de la información.

 

Uso de HTTP en lugar de HTTPS

 

  • Los datos se envían en texto plano. Ejemplo: contraseñas, tokens o información sensible pueden ser capturados por un atacante en la misma red.
  • Cifrado obsoleto o débil: TLS < 1.2, cifrados inseguros como RC4 o DES. No garantiza confidencialidad y autenticidad de los datos.
  • Certificados no validados o auto-firmados: Permite ataques de man-in-the-middle (MITM).

 

Log y trazas insuficientes

La falta de logs y trazas adecuadas es una vulnerabilidad que afecta la detección, investigación y respuesta a incidentes en APIs y aplicaciones backend. Sin registros detallados, un ataque puede pasar desapercibido, dificultando la auditoría y mitigación.

 

  • Logs insuficientes o incompletos: No registrar quién hizo qué, cuándo y desde dónde, solo se registran errores críticos, sin detalles de la operación.
  • Ausencia de trazabilidad: No se puede reconstruir la cadena de eventos, problema crítico en sistemas distribuidos o microservicios.
  • Almacenamiento inseguro de logs: Logs que incluyen datos sensibles sin encriptar, acceso libre para cualquier desarrollador o atacante interno.

 

Soluciones modernas

 

Autenticación y autorización

OAuth 2.0 para delegación (apps de terceros, microservicios), con PKCE en public clients y confidential clients con secreto.

 

JWT (RFC 7519): firmar (JWS) y, si es sensible, cifrar (JWE). Verificar iss, aud, exp; rotar claves (JWKS). Usar tokens cortos y refresh rotation. datatracker.ietf.org+1

 

Ejemplo (Node.js / Express): verificación estricta de JWT

 

import express from "express";
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";

const app = express();
const client = jwksClient({ jwksUri: "https://idp.example.com/.well-known/jwks.json" });

function getKey(header, cb) {
  client.getSigningKey(header.kid, (err, key) => {
    if (err) return cb(err);
    cb(null, key.getPublicKey());
  });
}

function requireAuth(audience, issuer) {
  return (req, res, next) => {
    const token = (req.headers.authorization || "").replace(/^Bearer /, "");
    jwt.verify(token, getKey, {
      algorithms: ["RS256","ES256"],
      audience,
      issuer,
      clockTolerance: 5
    }, (err, payload) => {
      if (err) return res.status(401).json({ error: "invalid_token" });
      req.user = payload;
      next();
    });
  };
}

app.get("/me", requireAuth("api://orders", "https://idp.example.com/"), (req, res) => {
  res.json({ sub: req.user.sub, roles: req.user.roles || [] });
});

 

Autorización por recurso (BOLA)

 

app.get("/orders/:id", requireAuth("api://orders", ISSUER), async (req, res) => {
  const order = await repo.getOrder(req.params.id);
  if (!order) return res.sendStatus(404);
  const isOwner = order.userId === req.user.sub;
  const isAdmin = (req.user.roles || []).includes("admin");
  if (!isOwner && !isAdmin) return res.sendStatus(403);
  res.json(order);
});

 

Nota: describir reglas en un PDP (Policy Decision Point) centralizado (p. ej., OPA/ABAC) y aplicar en cada microservicio (policy as code).

 

Validación de contratos y serialization hardening

 

1. Validación de contratos

Consiste en definir un esquema o contrato para los datos que la API recibe y devuelve, asegurando que:

  • Las solicitudes cumplen con el formato esperado.
  • Las respuestas solo incluyen campos autorizados y seguros.
  • Se evita el overposting (mass assignment) y errores por datos inesperados.

 

2. Serialization Hardening

Se refiere a controlar estrictamente cómo los objetos del backend se convierten en JSON u otros formatos, evitando:

  • Exposición de campos sensibles.
  • Serialización de propiedades internas o de bajo nivel que podrían ser explotadas.

 

Validación de entrada (contrato) con Joi

 

const Joi = require('joi');

const createUserSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).required()
});

app.post('/users', (req, res) => {
  const { error, value } = createUserSchema.validate(req.body);
  if (error) return res.status(400).json({ error: error.details[0].message });

  // Solo asignamos los campos permitidos (whitelist)
  const newUser = {
    id: users.length + 1,
    name: value.name,
    email: value.email,
    password: hashPassword(value.password),
    role: 'user' // no se puede pasar por request
  };
  users.push(newUser);
  res.status(201).json({ id: newUser.id, name: newUser.name, email: newUser.email });
});

 

Serialization Hardening

 

function serializeUser(user) {
  return {
    id: user.id,
    name: user.name,
    email: user.email
    // nunca incluir password ni role
  };
}

app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).send('Not found');
  res.json(serializeUser(user));
});

 

3. Rate limiting, quotas y resiliencia

Implementar umbral por IP/usuario/token; responder 429 con Retry-After y, si es posible, cabeceras RateLimit-* (borrador IETF).

Proteger endpoints costosos (búsquedas, exportaciones, webhooks salientes).

Añadir circuit breakers, timeouts y idempotencia en operaciones críticas (p. ej., reintentos seguros en PUT/DELETE por semántica HTTP). rfc-editor.orgdatatracker.ietf.org

 

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

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 min
  max: 5, // Máx. 5 intentos
  message: "Demasiados intentos, inténtalo más tarde"
});

app.use('/login', loginLimiter);
const userQuota = {};

app.use((req, res, next) => {
  const userId = req.user?.id || req.ip;
  userQuota[userId] = (userQuota[userId] || 0) + 1;
  
  if (userQuota[userId] > 1000) { // Máx. 1000 requests/día
    return res.status(429).send("Cuota diaria excedida");
  }
  next();
});

 

4. Transporte seguro y gestión de secretos

TLS 1.3 preferente; deshabilitar versiones antiguas y ciphers débiles, siguiendo RFC 9325 (BCP 195).

Opcional mTLS para integraciones de alto privilegio.

Rotación de certificados y secrets fuera del código (KMS, Vault). datatracker.ietf.org+1

 

// Node.js con HTTPS
const https = require('https');
https.createServer(credentials, app).listen(443);
#nginx
ssl_protocols TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

 

5. CORS y cabeceras de seguridad

Definir orígenes explícitos; no usar * con credentials: true.

Limitar métodos y cabeceras permitidos; cachear preflight con max-age razonable.

Añadir Content-Security-Policy, Referrer-Policy, X-Content-Type-Options, X-Frame-Options.

 

// Node.js
app.use(cors({
  origin: ['https://miapp.com', 'https://admin.miapp.com']
}));
# Rails
allow do
  origins 'https://miapp.com', 'https://admin.miapp.com'
  resource '*', headers: :any, methods: [:get, :post]
end

 

6 Observabilidad y respuesta a incidentes

 

  • Logs con: who/what/when/where, traceId/spanId, success/failure, lat/lon/IP (si procede), sin PII sensible.
  • Métricas: tasas de 2xx/4xx/5xx, latencia p95/p99, errores por operación, throttling hits.
  • Trazas distribuidas: instrumentar clientes y servidores; propagar traceparent.
  • Alertas por anomalías (cambios bruscos en 401/403, picos 429/5xx).

 

7. Zero Trust aplicado a APIs

 

  • Autenticación y autorización en cada peticion
  • Cada servicio o microservicio valida que la petición proviene de un cliente o servicio autorizado.
  • Ejemplo: cliente → API → microservicio → base de datos.
  • Principio de menor privilegio: Cada token o API key tiene acceso solo a los recursos que necesita. Reduce el impacto si un token se ve comprometido.
  • Validación constante: Revisar permisos, roles y scopes en cada llamada, no solo al inicio de sesión.
  • Seguridad en transporte: Todas las comunicaciones cifradas con TLS 1.2+. mTLS opcional para autenticar servicios internos.
  • Monitoreo y trazabilidad: Logs, trace IDs y alertas para cada petición sospechosa.

 

Node.js – Middleware Zero Trust

 

const jwt = require('jsonwebtoken');

function zeroTrust(requiredRole) {
  return (req, res, next) => {
    const token = req.headers['authorization']?.split(' ')[1];
    if(!token) return res.status(401).send('No token');

    try {
      const payload = jwt.verify(token, process.env.JWT_PUBLIC_KEY);
      if(!payload.roles.includes(requiredRole))
        return res.status(403).send('Forbidden');
      req.user = payload;
      next();
    } catch(err) {
      return res.status(401).send('Invalid token');
    }
  };
}

// Uso
app.get('/internal-data', zeroTrust('service_reader'), (req, res) => {
  res.json({ data: 'solo servicios autorizados' });
});

 

Playbook de endurecimiento para APIs (Hardened API y aplicaciones backend)

 

1. Autenticación y Autorización

  • Usar JWT o tokens OAuth 2.0 con expiración corta.
  • Implementar autenticación en cada salto (Zero Trust).
  • Aplicar principio de menor privilegio.
  • Reforzar endpoints críticos con MFA o mTLS.
  • Ejemplo Node.js: middleware que valida roles y scopes (ver sección Zero Trust).
  • Ejemplo Ruby: Pundit + JWT para autorización de endpoints.

 

2. Validación de Entrada y Salida

  • Definir contratos claros con esquemas (Joi, Zod, Dry::Validation).
  • Sanitizar y normalizar datos.
  • Evitar mass assignment y solo aceptar campos permitidos.
  • Serializar la salida con DTOs / Serializers para no exponer datos sensibles.

 

3. Protección de Datos

  • Usar HTTPS/TLS 1.2+ obligatorio.
  • Habilitar HSTS y considerar mTLS para microservicios internos.
  • Nunca exponer contraseñas, tokens o claves en logs o respuestas.

 

4. Control de Acceso a Recursos

  • Evitar Broken Object Level Authorization (BOLA/IDOR):
  • Validar que el recurso pertenece al usuario autenticado.
  • Usar identificadores no predecibles (UUIDs).
  • Implementar rate limiting y quotas para prevenir abusos.
  • Ejemplo Node.js: express-rate-limit.
  • Ejemplo Ruby: Rack::Attack.

 

5. Seguridad Operativa

  • Configurar logs estructurados y trazables.
  • Incluir usuario, IP, endpoint, timestamp, status.
  • Monitoreo y alertas para actividades sospechosas.
  • Evitar exposición de información sensible en logs.

 

6. CORS y Seguridad del Frontend

  • Configurar orígenes permitidos estrictamente.
  • Limitar métodos HTTP y cabeceras.
  • No usar "*" en APIs que manejan datos sensibles.

 

7. Endurecimiento de Serialización

  • Solo serializar los campos necesarios.
  • Validar esquemas de salida (Output DTO/Serializer).
  • Prevenir exposición de campos internos o metadatos.

 

8. Pruebas y Auditoría

  • Pruebas automatizadas de autorización, validación de entrada y salida.
  • Escaneo de vulnerabilidades con OWASP ZAP, Burp Suite.
  • Revisiones de código centradas en seguridad (SAST).

 

9. Zero Trust Aplicado

  • Cada servicio y usuario verificado en cada petición.
  • Tokens con scopes mínimos.
  • Logs y trazabilidad por cada request.
  • Revocación inmediata de credenciales comprometidas.

 

10. Checklist de Hardened API

ÁreaAcción
AutenticaciónJWT/OAuth2, MFA, exp. corta
AutorizaciónValidación BOLA, scopes, Zero Trust
Input/OutputEsquemas, sanitización, whitelist
TransporteHTTPS, HSTS, mTLS
LoggingEstructurado, centralizado, seguro
Rate LimitingLímites por IP/usuario, quotas
CORSOrígenes y métodos permitidos
SerializaciónDTOs, ocultar campos sensibles
TestingSAST, DAST, unit + integration tests