API Security · IPSmart

La biblia de la seguridad en la web

Lo que no se puede cometer al construir una aplicación o una API, en reglas numeradas: qué prohíbe cada una, por qué revienta cuando se incumple, qué se pone en su lugar y cómo lo encuentra un pentest. Debajo, el boletín de las últimas vulnerabilidades publicadas.

50 reglas 13 categorías 0 vulnerabilidades en el boletín Acceso público, sin token

Inyección

5 reglas
1

Nunca concatenes entrada del usuario dentro de una consulta SQL

Por qué falla

El motor no distingue entre tu consulta y el texto que pegó el atacante: todo llega como una sola instrucción. Con un solo campo mal tratado se extrae la tabla de usuarios completa, se borran registros o se escribe un fichero en el servidor.

En su lugar

Usa consultas parametrizadas o el query builder del ORM, siempre con marcadores de posición. Los identificadores que no se pueden parametrizar (nombre de tabla, columna de ordenación) se resuelven contra una lista blanca cerrada, nunca con el valor recibido.

Mal
db.query('SELECT * FROM users WHERE email = ' + "'" + email + "'")
Bien
db.query('SELECT * FROM users WHERE email = $1', [email])
Cómo se detecta

Se inyectan comillas, comentarios y payloads booleanos y temporales (OR 1=1, SLEEP) en cada parámetro, cabecera y cookie, y se comparan tiempos de respuesta y diferencias en el cuerpo. Herramientas como sqlmap automatizan la confirmación ciega.

OWASP A03:2021 · CWE-89
2

Nunca pases datos del usuario a un intérprete de comandos del sistema operativo

Por qué falla

Un punto y coma, un pipe o unas comillas invertidas convierten tu utilidad de conversión de imágenes en una shell remota. Es la escalada más directa que existe: de un campo de formulario a ejecución de código en el servidor.

En su lugar

Llama al binario sin shell, pasando los argumentos como array (execFile / spawn sin shell:true). Si el valor decide qué se ejecuta, valídalo contra una lista blanca. Ejecutar una shell con texto compuesto por el usuario no tiene una versión segura.

Mal
exec('convert ' + archivo + ' salida.png')
Bien
execFile('convert', [archivo, 'salida.png'])
Cómo se detecta

Se prueban separadores de comando y sustituciones en cada parámetro que huela a nombre de fichero, host o formato, y se busca ejecución fuera de banda: una resolución DNS o una petición HTTP hacia el servidor del auditor.

OWASP A03:2021 · CWE-78
3

Nunca construyas un filtro NoSQL con un objeto JSON del cliente sin validar su forma

Por qué falla

Si el cliente puede enviar un objeto donde esperabas una cadena, cuela operadores del motor. Un password con {"$ne": null} convierte tu comprobación de credenciales en un "cualquier contraseña sirve".

En su lugar

Valida el cuerpo con un esquema estricto (zod, JSON Schema de Fastify) que exija tipos primitivos y rechace propiedades desconocidas. Nunca esparzas el cuerpo recibido dentro del filtro de la consulta.

Mal
users.findOne({ email: body.email, password: body.password })
Bien
const { email, password } = Esquema.parse(body); users.findOne({ email })
Cómo se detecta

Se sustituyen valores escalares por objetos con operadores del motor ($ne, $gt, $regex, $where) en login y en endpoints de búsqueda, y se observa si el conjunto de resultados cambia o si el login pasa sin credenciales válidas.

OWASP A03:2021 · CWE-943
4

Nunca metas entrada del usuario dentro de una plantilla que se evalúa en el servidor

Por qué falla

El motor de plantillas es un intérprete completo. Si el atacante controla la plantilla y no solo los datos, tiene acceso al contexto de ejecución del proceso y de ahí a variables de entorno, secretos y ejecución de código.

En su lugar

La plantilla es siempre una constante del código; el dato del usuario entra únicamente como variable de contexto. Si el producto permite plantillas editables por el cliente, ejecútalas en un motor sin acceso a objetos del lenguaje (sandbox lógico) y con lista blanca de funciones.

Cómo se detecta

Se envían expresiones propias de cada motor ({{7*7}}, ${7*7}, #{7*7}) en campos que acaban en correos, PDF, informes o asuntos de notificación, y se comprueba si el resultado aparece evaluado.

OWASP A03:2021 · CWE-1336
5

Nunca uses eval, new Function ni deserialización insegura sobre datos externos

Por qué falla

Deserializar un objeto arbitrario permite al atacante elegir qué clases se instancian y qué métodos se disparan durante la reconstrucción. Es ejecución de código sin necesitar una vulnerabilidad de memoria.

En su lugar

Transporta datos en formatos sin comportamiento: JSON parseado con JSON.parse y validado contra un esquema. Si necesitas objetos ricos, reconstrúyelos a mano desde campos validados. Nada de eval ni de deserializadores que resucitan tipos por nombre.

Cómo se detecta

Se buscan cookies, campos ocultos y cabeceras con blobs codificados en base64 que empiezan por marcas conocidas de serialización, y se sustituyen por cadenas de gadgets. También se audita el código en busca de eval, Function y require dinámico.

OWASP A08:2021 · CWE-502

Autenticación y sesiones

6 reglas
6

Nunca guardes contraseñas en texto plano ni con un hash rápido como MD5 o SHA-1

Por qué falla

Cuando se filtre la base de datos —y las bases de datos se filtran— un hash rápido se rompe a millones de intentos por segundo con hardware barato. Como la gente reutiliza contraseñas, tu fuga se convierte en la fuga del banco de tus usuarios.

En su lugar

Usa un algoritmo diseñado para ser lento y costoso en memoria: argon2id, scrypt o bcrypt, con sal por usuario y parámetros revisados cada año. Guarda el algoritmo y sus parámetros junto al hash para poder migrar sin forzar un reseteo global.

Mal
const hash = createHash('md5').update(password).digest('hex')
Bien
const hash = await argon2.hash(password, { type: argon2.argon2id })
Cómo se detecta

Se revisa el esquema y el código de registro. En caja negra, hashes de 32 caracteres hexadecimales que aparecen en respuestas, exportaciones o copias de seguridad delatan MD5 al instante.

OWASP A02:2021 · CWE-916
7

Nunca permitas intentos de inicio de sesión ilimitados

Por qué falla

Sin freno, la fuerza bruta y el credential stuffing con listas filtradas son cuestión de tiempo de CPU ajeno. El atacante no necesita romper nada: prueba hasta acertar, y con listas reales acierta.

En su lugar

Limita por cuenta y por IP con retardo creciente, bloqueo temporal y captcha tras varios fallos. Añade segundo factor para roles con privilegios. Registra el evento y alerta cuando una misma IP falla contra muchas cuentas distintas.

Cómo se detecta

El auditor lanza cientos de intentos fallidos seguidos y observa si llega respuesta 200 siempre, si el tiempo de respuesta no cambia y si nunca aparece un 429 ni un bloqueo.

OWASP A07:2021 · CWE-307
8

Nunca reutilices el identificador de sesión después de iniciar sesión

Por qué falla

Si el identificador anónimo sobrevive al login, quien haya conseguido plantarlo antes (por un enlace o por un dominio hermano) queda dentro de la sesión autenticada de la víctima. Se llama fijación de sesión y no requiere robar nada.

En su lugar

Regenera el identificador de sesión en el login, en el cambio de contraseña y en cualquier cambio de privilegios, e invalida el anterior en el servidor. Marca la cookie como HttpOnly, Secure y SameSite=Lax o Strict.

Cómo se detecta

Se anota la cookie antes de autenticarse, se inicia sesión y se compara: si el valor es el mismo, la fijación está confirmada. También se comprueba si la cookie vieja sigue siendo válida tras cerrar sesión.

OWASP A07:2021 · CWE-384
9

Nunca emitas tokens de sesión o JWT sin caducidad ni forma de revocarlos

Por qué falla

Un token eterno es una llave que no se puede cambiar. Si se filtra en un log, en una captura o en el historial del navegador, el acceso del atacante dura lo que dure el sistema, y despedir a un empleado no cierra su acceso.

En su lugar

Vida corta para el token de acceso (minutos), refresco rotatorio con detección de reutilización, y una lista de revocación o versión de credencial por usuario que invalide todo lo emitido antes. Cerrar sesión tiene que invalidar en el servidor, no solo borrar en el cliente.

Cómo se detecta

Se decodifica el token y se mira exp. Después se cierra sesión y se reutiliza el token capturado: si sigue funcionando, no hay revocación real.

OWASP A07:2021 · CWE-613
10

Nunca aceptes un JWT sin verificar su firma y sin fijar el algoritmo esperado

Por qué falla

Si aceptas el algoritmo que declara el propio token, el atacante pone alg: none o cambia RS256 por HS256 y firma con tu clave pública, que es información abierta. Se fabrica un token de administrador sin conocer ningún secreto.

En su lugar

Verifica siempre con una lista fija de algoritmos permitidos, y valida además issuer, audience y expiración. Nunca uses decode sin verify para tomar decisiones. Las claves de firma van en el gestor de secretos y rotan.

Mal
const payload = jwt.decode(token)
Bien
const payload = jwt.verify(token, clave, { algorithms: ['RS256'], issuer, audience })
Cómo se detecta

Se altera el payload sin tocar la firma, se prueba alg: none y la confusión RS256/HS256, y se comprueba si el servidor sigue aceptando el token.

OWASP A02:2021 · CWE-347
11

Nunca reveles si falló el usuario o la contraseña, ni en el mensaje ni en el tiempo

Por qué falla

Mensajes distintos convierten el login en un enumerador de cuentas: el atacante confirma qué correos existen y concentra el ataque solo en los válidos. El mismo problema aparece en el registro y en el "he olvidado mi contraseña".

En su lugar

Una única respuesta genérica ("credenciales inválidas") para todos los casos, y el mismo trabajo de cómputo en ambos caminos: calcula el hash incluso cuando el usuario no existe para que el tiempo no delate. En recuperación, responde siempre "si el correo existe, hemos enviado un enlace".

Cómo se detecta

Se comparan respuestas y latencias con un usuario conocido y otro inventado. Diferencias de texto, de código HTTP o de milisegundos consistentes bastan para enumerar.

OWASP A07:2021 · CWE-204

Control de acceso

5 reglas
12

Nunca confíes en el identificador de recurso que envía el cliente sin comprobar que le pertenece

Por qué falla

Cambiar /facturas/1041 por /facturas/1042 no requiere herramientas. Si la consulta solo filtra por id y no por dueño, cualquier usuario autenticado lee, edita o borra los datos de todos los demás. Es la vulnerabilidad número uno en APIs reales.

En su lugar

La comprobación de propiedad va en la consulta, no en un if posterior: filtra siempre por el identificador del sujeto autenticado además del identificador del recurso. Centraliza la decisión en una capa de autorización que no se pueda olvidar por endpoint.

Mal
facturas.findById(req.params.id)
Bien
facturas.findOne({ id: req.params.id, empresaId: req.user.empresaId })
Cómo se detecta

Se abren dos sesiones de usuarios distintos y se intercambian identificadores en rutas, cuerpos y parámetros. Cualquier 200 que devuelva datos ajenos es un hallazgo crítico.

OWASP API1:2023 · CWE-639
13

Nunca decidas permisos en el frontend

Por qué falla

Ocultar un botón no protege nada: el endpoint sigue ahí y se llama con curl. El JavaScript del cliente está en manos del atacante, y con él todas las banderas de rol que hayas dejado en el estado de la aplicación.

En su lugar

El servidor decide y el cliente solo dibuja. Cada endpoint valida rol y permiso por su cuenta, sin depender de por dónde vino la llamada. Lo del frontend es comodidad visual, jamás control.

Cómo se detecta

Se listan las rutas de la API en el bundle de JavaScript y se invocan directamente con una sesión de usuario sin privilegios, saltándose por completo la interfaz.

OWASP A01:2021 · CWE-602
14

Nunca dejes que el usuario elija su rol, su plan o su empresa en el cuerpo de la petición

Por qué falla

Si el registro o el perfil aceptan un campo rol y lo persisten, cualquiera se convierte en administrador con una línea de curl. Lo mismo con empresaId: se salta el aislamiento entre clientes.

En su lugar

Los campos que otorgan privilegio no se aceptan nunca desde la petición: se derivan del token o los asigna un flujo administrativo con su propia autorización y su registro de auditoría. Usa listas de campos permitidos por endpoint y por rol.

Cómo se detecta

Se añaden campos privilegiados al cuerpo de peticiones de registro y actualización de perfil y se comprueba si quedan reflejados al recargar el propio usuario.

OWASP A01:2021 · CWE-269
15

Nunca dejes un endpoint sin comprobación de autorización porque "es una ruta interna"

Por qué falla

Lo interno acaba expuesto: un balanceador mal configurado, un ingress nuevo o una migración a la nube publican esa ruta. La seguridad por desconocimiento de la URL dura hasta el primer escaneo de directorios.

En su lugar

Deniega por defecto: el middleware exige autenticación en todo salvo en una lista explícita y corta de rutas públicas. Las rutas de administración y de servicio llevan además autorización propia y, si aplica, restricción de red.

Cómo se detecta

Se enumeran rutas con diccionarios y con las que aparecen en la documentación OpenAPI, y se llaman sin credenciales. Cualquier 200 sin autenticar que no sea deliberadamente público es un hallazgo.

OWASP A01:2021 · CWE-306
16

Nunca omitas el filtro por empresa en una consulta de un sistema multiempresa

Por qué falla

Una sola consulta sin el filtro de tenant mezcla los datos de dos clientes. En un producto vendido a varias empresas, eso no es un bug: es una brecha contractual y de protección de datos que se ve en pantalla.

En su lugar

Aplica el aislamiento en una capa que no se pueda saltar: ámbito por defecto en el ORM, políticas de fila en la base de datos (RLS) o un repositorio que reciba siempre el contexto del tenant. Prohíbe las consultas crudas sin ese filtro y cúbrelo con pruebas automáticas.

Cómo se detecta

Con dos empresas cargadas de datos, se recorren todos los listados y exportaciones con la sesión de una y se busca cualquier registro de la otra. También se prueban los identificadores de la empresa ajena en rutas directas.

OWASP A01:2021 · CWE-1230

Criptografía y secretos

5 reglas
17

Nunca escribas claves, tokens ni contraseñas en el código o en el repositorio

Por qué falla

Un secreto en git está en cada clon, en cada fork y en el historial para siempre; borrarlo del último commit no lo borra. Los bots rastrean repositorios públicos y usan las claves en minutos.

En su lugar

Los secretos van por variable de entorno o gestor de secretos, y el fichero de ejemplo lleva solo nombres, no valores. Añade escaneo de secretos en el gancho de pre-commit y en el pipeline. Si uno se filtró, la única respuesta válida es rotarlo.

Cómo se detecta

Se escanea el historial completo del repositorio con detectores de patrones y entropía, y se buscan claves en los bundles de frontend, en imágenes de contenedor y en ficheros de configuración desplegados.

OWASP A05:2021 · CWE-798
18

Nunca inventes tu propio cifrado ni uses algoritmos y modos obsoletos

Por qué falla

DES, RC4 y el modo ECB están rotos en la práctica: ECB deja ver patrones del texto original en el cifrado. Un esquema casero sin autenticación permite además que el atacante modifique el mensaje cifrado sin que lo notes.

En su lugar

Usa cifrado autenticado estándar de la biblioteca del sistema: AES-256-GCM o ChaCha20-Poly1305, con nonce único por mensaje y claves gestionadas fuera del código. Para transporte, TLS 1.2 o superior con suites modernas.

Cómo se detecta

Se revisan las llamadas criptográficas del código y se analiza el TLS del servicio con escáneres de suites. Bloques cifrados repetidos ante entradas repetidas delatan ECB.

OWASP A02:2021 · CWE-327
19

Nunca generes tokens, códigos OTP ni enlaces de recuperación con Math.random

Por qué falla

Math.random es un generador predecible pensado para simulaciones, no para secretos. Con unas pocas muestras se reconstruye su estado y se predicen los siguientes valores: el enlace de recuperación de otra persona se adivina en lugar de robarse.

En su lugar

Usa un generador criptográficamente seguro (crypto.randomBytes o crypto.randomUUID) con al menos 128 bits de entropía, y dale al token de recuperación una caducidad corta y un solo uso.

Mal
const token = Math.random().toString(36).slice(2)
Bien
const token = crypto.randomBytes(32).toString('hex')
Cómo se detecta

Se recogen cientos de tokens emitidos y se analiza su distribución y secuencialidad. Longitudes cortas, prefijos comunes o correlación con la marca de tiempo confirman la predictibilidad.

OWASP A02:2021 · CWE-338
20

Nunca compares secretos con el operador de igualdad normal

Por qué falla

La comparación de cadenas corta en el primer carácter distinto, así que tarda un poco más cuantos más caracteres iniciales acierta el atacante. Medido muchas veces, ese microsegundo permite reconstruir el token carácter a carácter.

En su lugar

Compara en tiempo constante con crypto.timingSafeEqual sobre búferes de la misma longitud, o compara hashes de igual tamaño de ambos valores. Aplica lo mismo a tokens de API, firmas de webhook y códigos OTP.

Mal
if (tokenRecibido === tokenEsperado) { ... }
Bien
if (crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))) { ... }
Cómo se detecta

Se envían miles de tokens que varían un carácter y se mide la latencia con precisión estadística; una diferencia consistente y creciente confirma la fuga por tiempo.

OWASP A02:2021 · CWE-208
21

Nunca transmitas credenciales ni datos personales sobre HTTP sin cifrar

Por qué falla

En una red compartida, cualquiera en el mismo camino lee y modifica el tráfico en claro. Basta una redirección inicial sin cifrar para que un atacante mantenga a la víctima en HTTP y capture la sesión entera.

En su lugar

HTTPS obligatorio en todo el dominio, redirección permanente desde HTTP y cabecera HSTS con preload. Cookies con el atributo Secure. El cifrado también aplica entre servicios internos y hacia la base de datos.

Cómo se detecta

Se comprueba si el puerto 80 sirve contenido en lugar de redirigir, si falta HSTS, si hay recursos mixtos y si alguna cookie de sesión viaja sin Secure.

OWASP A02:2021 · CWE-319

Configuración y exposición

4 reglas
22

Nunca dejes el modo depuración ni las trazas de error activas en producción

Por qué falla

La página de error de depuración entrega el mapa completo: rutas del sistema de ficheros, versiones, fragmentos de código, consultas y a veces variables de entorno con credenciales. Es reconocimiento gratis para el atacante.

En su lugar

NODE_ENV en producción, trazas solo al log interno y al cliente un mensaje genérico con un identificador de correlación. Verifica la configuración efectiva en el entorno desplegado, no solo en el fichero de ejemplo.

Cómo se detecta

Se provocan errores a propósito: tipos inválidos, cuerpos malformados, rutas inexistentes, y se mira si la respuesta trae traza, versión del framework o rutas absolutas del servidor.

OWASP A05:2021 · CWE-489
23

Nunca publiques paneles de administración, métricas o documentación de la API sin autenticar

Por qué falla

Un /metrics abierto revela volumen de negocio y rutas internas; un Swagger abierto entrega el catálogo completo de endpoints y parámetros; un panel de administración abierto es directamente la entrada.

En su lugar

Todo lo operativo va detrás de autenticación y, cuando sea posible, restringido por red o VPN. La documentación pública de la API se publica deliberadamente y recortada, nunca por defecto.

Cómo se detecta

Se prueban rutas conocidas (/metrics, /actuator, /swagger, /docs, /admin, /.git, /.env) y se cruzan con buscadores de servicios expuestos para ver qué llegó a indexarse.

OWASP A05:2021 · CWE-1188
24

Nunca combines un CORS con origen comodín o reflejado con credenciales

Por qué falla

Reflejar el Origin recibido y permitir credenciales equivale a dejar que cualquier web lea las respuestas autenticadas de tus usuarios desde su navegador. El atacante monta una página y roba datos de quien la visite estando logueado.

En su lugar

Lista blanca explícita de orígenes, comparación exacta (ojo con los sufijos: midominio.com.atacante.com), y credentials solo con esos orígenes concretos. Si la API es realmente pública, entonces no lleva credenciales.

Cómo se detecta

Se envía un Origin arbitrario y se observa si vuelve reflejado en Access-Control-Allow-Origin junto con Allow-Credentials: true. También se prueban subdominios y sufijos parecidos al dominio legítimo.

OWASP A05:2021 · CWE-942
25

Nunca sirvas la aplicación sin cabeceras de seguridad

Por qué falla

Sin Content-Security-Policy, un XSS que aparezca mañana se ejecuta sin resistencia. Sin X-Content-Type-Options, el navegador adivina el tipo y convierte un fichero subido en un script. Sin X-Frame-Options o frame-ancestors, tu panel se enmarca en una web ajena para clickjacking.

En su lugar

Aplica un paquete de cabeceras seguras (helmet o equivalente) y define una CSP concreta, sin comodines ni unsafe-inline, y HSTS. Que la política sea real y no una cabecera vacía: revísala en la respuesta desplegada.

Cómo se detecta

Se inspeccionan las cabeceras de respuesta de las páginas principales y se puntúa la CSP buscando unsafe-inline, unsafe-eval y orígenes comodín.

OWASP A05:2021 · CWE-693

XSS

4 reglas
26

Nunca insertes texto del usuario en el DOM con innerHTML

Por qué falla

El navegador interpreta como marcado lo que le des. Un comentario, un nombre o un campo de perfil con etiquetas se convierte en código que corre con la sesión de quien lo lea: roba la sesión, cambia el correo de la cuenta y actúa en nombre de la víctima.

En su lugar

Usa textContent, o el escapado automático del framework (interpolación de React, Angular o Vue). Si de verdad necesitas HTML enriquecido, pásalo por un saneador con lista blanca (DOMPurify) y refuérzalo con una CSP estricta.

Mal
div.innerHTML = "Hola " + nombre
Bien
div.textContent = "Hola " + nombre
Cómo se detecta

Se inyectan cargas en cada campo y parámetro que acabe pintado, incluidos los que se ven en el panel de administración, y se comprueba si se ejecutan al recargar (almacenado) o al abrir el enlace preparado (reflejado).

OWASP A03:2021 · CWE-79
27

Nunca coloques datos del usuario dentro de una etiqueta script o de un atributo de evento

Por qué falla

Dentro de un bloque de script el escapado de HTML no sirve: basta cerrar la cadena o la propia etiqueta para salir al contexto de código. Es la forma más común de que un escapado "que ya estaba puesto" no proteja nada.

En su lugar

Pasa los datos al cliente como JSON en un elemento con type="application/json" y léelos con JSON.parse, o por atributos data- escapados como HTML. El escapado depende del contexto: HTML, atributo, URL y JavaScript no se escapan igual.

Cómo se detecta

Se busca en el código fuente servido dónde aparecen valores dinámicos dentro de scripts o de atributos onclick/onerror, y se prueban cargas que cierran la cadena y la etiqueta.

OWASP A03:2021 · CWE-79
28

Nunca uses una URL del usuario en un href o un src sin validar el esquema

Por qué falla

Un enlace con javascript: o con data:text/html ejecuta código al pulsarlo, aunque hayas escapado el HTML correctamente. Un src remoto además filtra a terceros quién visita tu página.

En su lugar

Valida que la URL empiece por http: o https: (parseándola, no con una comparación de texto), y para enlaces externos añade rel="noopener noreferrer". Si el destino se guarda, valídalo al escribir y al pintar.

Cómo se detecta

Se guardan URLs con esquemas peligrosos en campos de perfil, avatar o enlace de empresa y se comprueba si el atributo generado las conserva.

OWASP A03:2021 · CWE-83
29

Nunca guardes el token de sesión en localStorage si puedes usar una cookie HttpOnly

Por qué falla

Todo lo que hay en localStorage lo lee cualquier script de la página, incluidos los de terceros y el que entre por un XSS. Una cookie HttpOnly no es accesible desde JavaScript, así que un XSS ya no se lleva la sesión de recuerdo.

En su lugar

Cookie HttpOnly, Secure y SameSite para la sesión, con protección CSRF acompañando. Si el arquitectura obliga a un token en el cliente, que sea de vida muy corta, con refresco rotatorio y ámbito mínimo.

Cómo se detecta

Se abre el almacenamiento del navegador tras iniciar sesión y se busca el token; después se comprueba desde la consola si un script cualquiera puede leerlo.

OWASP A07:2021 · CWE-522

CSRF

2 reglas
30

Nunca hagas que una operación que cambia estado dependa solo de la cookie de sesión

Por qué falla

El navegador adjunta la cookie automáticamente, también cuando la petición la origina una web ajena. Una página maliciosa envía un formulario a tu endpoint y la víctima, autenticada, transfiere dinero o cambia su correo sin enterarse.

En su lugar

Token anti-CSRF por sesión y por formulario, verificado en el servidor, más cookies SameSite=Lax o Strict. En APIs con token en cabecera Authorization el riesgo desaparece, pero entonces no debe aceptarse también la cookie como alternativa.

Cómo se detecta

Se reproduce una petición sensible desde un origen externo sin el token anti-CSRF o con uno de otra sesión, y se comprueba si el servidor la ejecuta igualmente.

OWASP A01:2021 · CWE-352
31

Nunca uses GET para operaciones que modifican o borran datos

Por qué falla

Un GET se dispara desde una etiqueta de imagen, un prefetch del navegador o un rastreador. Además queda escrito en logs, historiales y referers, con sus parámetros incluidos. Un enlace de borrado por GET se activa solo.

En su lugar

Verbo según el efecto: GET solo lee y es idempotente; POST, PUT, PATCH y DELETE para lo que cambia estado, con protección anti-CSRF. Los datos sensibles van en el cuerpo, nunca en la ruta ni en la query.

Cómo se detecta

Se revisa el listado de rutas buscando GET con verbos de acción (/borrar, /aprobar, /activar) y se comprueba si ejecutan el cambio al invocarlas desde el navegador.

OWASP A04:2021 · CWE-650

SSRF

2 reglas
32

Nunca hagas una petición saliente a una URL enviada por el usuario sin lista blanca

Por qué falla

Tu servidor está dentro de la red privada, así que se usa como proxy para alcanzar lo que el atacante no puede: bases de datos internas, paneles sin autenticar, servicios en localhost. La respuesta vuelve envuelta en tu propia API.

En su lugar

Lista blanca de dominios y esquemas permitidos, resolución del nombre y comprobación de que la IP resultante no es privada, de bucle local ni de enlace local, y esa comprobación aplicada tras cada redirección. Sin redirecciones automáticas y con tiempo de espera corto.

Cómo se detecta

Se apuntan los campos de URL (webhooks, importadores, previsualizadores, avatares por URL) a un servidor de escucha del auditor y a direcciones internas, y se observa si llega la petición o si cambian los tiempos de respuesta.

OWASP A10:2021 · CWE-918
33

Nunca dejes que el servicio pueda alcanzar el endpoint de metadatos del proveedor cloud

Por qué falla

La dirección de metadatos de la nube devuelve credenciales temporales del rol de la instancia. Un SSRF que la alcance no roba datos: roba la identidad del servicio y con ella el acceso a todo lo que ese rol puede tocar.

En su lugar

Bloquea el rango de enlace local por firewall o egress, exige la versión con token del servicio de metadatos, y da al rol de la instancia el mínimo permiso posible. Las llamadas salientes de la aplicación pasan por un proxy con lista blanca.

Cómo se detecta

Desde cualquier vector SSRF confirmado se solicita la ruta de metadatos y sus variantes con redirección, DNS que resuelve a la dirección interna y notaciones alternativas de IP.

OWASP A10:2021 · CWE-918

Ficheros

4 reglas
34

Nunca construyas una ruta de fichero concatenando lo que envía el usuario

Por qué falla

Con suficientes ../ se sale del directorio previsto y se lee cualquier fichero que el proceso pueda abrir: configuración, claves privadas, la base de datos SQLite. En escritura, se sobreescriben ficheros del sistema.

En su lugar

No aceptes rutas: acepta un identificador y resuélvelo contra un catálogo. Si es inevitable, normaliza con path.resolve y verifica que el resultado sigue dentro del directorio base antes de abrir. Decodifica una sola vez y rechaza secuencias sospechosas en lugar de limpiarlas.

Mal
fs.readFile(path.join(BASE, req.query.archivo))
Bien
const p = path.resolve(BASE, nombre); if (!p.startsWith(BASE + path.sep)) throw new Error('ruta invalida')
Cómo se detecta

Se prueban secuencias de subida de directorio, con codificación simple y doble, barras invertidas y rutas absolutas, en cada parámetro de descarga, plantilla, idioma o nombre de fichero.

OWASP A01:2021 · CWE-22
35

Nunca aceptes una subida fiándote de la extensión o del Content-Type declarados

Por qué falla

Ambos los elige el cliente. Un fichero llamado foto.png con contenido de script y Content-Type falseado pasa cualquier comprobación basada en el nombre. Si acaba en un directorio que ejecuta código, es ejecución remota.

En su lugar

Valida el contenido real (número mágico), fija un límite de tamaño y una lista blanca corta de tipos, renombra el fichero a un identificador generado por ti conservando solo la extensión permitida, y reprocesa las imágenes para descartar cargas embebidas.

Cómo se detecta

Se suben ficheros con doble extensión, con byte nulo, con Content-Type manipulado y con contenido ejecutable disfrazado, y se intenta después solicitar la URL resultante.

OWASP A04:2021 · CWE-434
36

Nunca sirvas los ficheros subidos desde el mismo dominio y directorio que la aplicación

Por qué falla

Compartir dominio significa compartir cookies y origen: un HTML o un SVG subido ejecuta scripts con acceso a la sesión de tus usuarios. Y si el directorio es el mismo que sirve la aplicación, el servidor puede acabar interpretando lo subido.

En su lugar

Guarda fuera de la raíz web, sirve desde un dominio o bucket separado, con Content-Type fijado, X-Content-Type-Options: nosniff y Content-Disposition: attachment para lo que no sea imagen. Entrega mediante URLs firmadas y de vida corta.

Cómo se detecta

Se sube un SVG o un HTML con script y se abre su URL directa para ver si se ejecuta en el dominio de la aplicación y si la cookie de sesión es accesible desde ahí.

OWASP A05:2021 · CWE-646
37

Nunca proceses XML de terceros con las entidades externas habilitadas

Por qué falla

Una entidad externa hace que el parser lea ficheros locales o pida URLs internas mientras interpreta el documento. Sirve para leer /etc/passwd, para SSRF y para tumbar el proceso con una expansión recursiva.

En su lugar

Desactiva DTD y entidades externas en el parser, o cambia a JSON con esquema. Aplica lo mismo a los formatos que llevan XML dentro: documentos ofimáticos, SVG, feeds y SOAP.

Cómo se detecta

Se envía un XML con una entidad que apunta a un fichero local y a un servidor del auditor, y se observa si el contenido aparece en la respuesta o si llega la petición fuera de banda.

OWASP A05:2021 · CWE-611

Dependencias y cadena de suministro

3 reglas
38

Nunca instales dependencias sin lockfile ni versiones fijadas

Por qué falla

Sin lockfile, cada despliegue puede traer código distinto del que probaste. Un mantenedor comprometido o un paquete con nombre parecido al que querías entra en producción sin que nadie apruebe nada.

En su lugar

Commitea el lockfile, instala con el modo reproducible (npm ci) y revisa las dependencias nuevas antes de aceptarlas: quién la mantiene, cuánto pesa el árbol que arrastra, si de verdad la necesitas. Menos dependencias es menos superficie.

Cómo se detecta

Se compara el árbol instalado con el lockfile, se buscan rangos abiertos en el manifiesto y paquetes con nombres sospechosamente parecidos a otros populares.

OWASP A06:2021 · CWE-1104
39

Nunca ignores los avisos de vulnerabilidad de tus dependencias

Por qué falla

Una CVE publicada es una guía de explotación con exploit incluido. La ventana entre la publicación y el escaneo masivo se mide en horas, y tu versión vulnerable está anunciada en la propia respuesta del servidor.

En su lugar

Auditoría automática en cada pipeline que rompa la construcción ante severidad alta, inventario de componentes (SBOM) actualizado y un responsable con plazo para parchear. Suscríbete a los avisos de lo que realmente usas.

Cómo se detecta

Se identifican versiones por banners, rutas de assets y respuestas de error, y se cruzan con las bases públicas de CVE. Este servicio existe precisamente para automatizar ese cruce.

OWASP A06:2021 · CWE-1395
40

Nunca des a scripts de terceros acceso a los secretos de producción en el pipeline

Por qué falla

Un postinstall de cualquier paquete del árbol se ejecuta con las variables del entorno de construcción. Si ahí viven las claves de producción, un solo paquete comprometido las exfiltra sin tocar tu código.

En su lugar

Separa los secretos por etapa y por entorno, no expongas los de producción en trabajos de construcción o de pruebas, usa credenciales de vida corta e identidad federada, y bloquea los scripts de instalación cuando puedas (--ignore-scripts).

Cómo se detecta

Se revisa la definición del pipeline buscando secretos globales, se comprueba si las ramas o los pull requests de forks pueden leerlos y se auditan los scripts de ciclo de vida del árbol de dependencias.

OWASP A08:2021 · CWE-506

Registro, errores y datos personales

3 reglas
41

Nunca registres contraseñas, tokens, cookies ni documentos de identidad en los logs

Por qué falla

Los logs se copian a herramientas de terceros, se ven en pantallas compartidas y los lee gente que no tendría acceso a esos datos. Un token en un log es una credencial válida guardada en un sitio sin control de acceso equivalente.

En su lugar

Lista de campos a enmascarar aplicada en el propio logger (password, token, authorization, cookie, tarjeta, documento), registro del identificador en lugar del dato, y retención limitada. Nunca vuelques el cuerpo completo de una petición sin filtrar.

Cómo se detecta

Se generan peticiones con valores marcados y se buscan literalmente en los logs, en la herramienta de observabilidad y en los mensajes de error de las integraciones.

OWASP A09:2021 · CWE-532
42

Nunca devuelvas al cliente el error crudo de la base de datos o del framework

Por qué falla

El mensaje del motor revela nombres de tablas y columnas, el dialecto y la consulta que falló. Es exactamente el mapa que le falta al atacante para afinar una inyección que iba a ciegas.

En su lugar

Un manejador de errores único que traduce cualquier excepción a un código y un mensaje neutros para el cliente, con un identificador de correlación, y que deja el detalle solo en el log interno.

Cómo se detecta

Se envían tipos inválidos, cadenas larguísimas, comillas y JSON malformado a todos los endpoints y se revisan las respuestas 400 y 500 en busca de detalles internos.

OWASP A09:2021 · CWE-209
43

Nunca dejes las acciones sensibles sin traza de auditoría

Por qué falla

Sin registro de quién hizo qué y cuándo, un incidente no se puede investigar ni acotar: no sabes qué datos salieron ni desde cuándo. Y sin alertas, la intrusión se descubre meses después y por un tercero.

En su lugar

Registra autenticaciones, cambios de permisos, accesos a datos personales, exportaciones y operaciones administrativas, con actor, recurso, origen y momento, en un almacén que la aplicación no pueda modificar. Añade alertas sobre patrones anómalos.

Cómo se detecta

El auditor ejecuta acciones sensibles y después pide las trazas: si su actividad no aparece, o si con acceso a la aplicación puede borrarla, hay hallazgo.

OWASP A09:2021 · CWE-778

APIs

4 reglas
44

Nunca expongas una API sin límite de peticiones por cliente

Por qué falla

Sin límite, el mismo endpoint sirve para enumerar usuarios, adivinar códigos, vaciar tu base de datos con paginación y arruinarte la factura de la nube. También lo convierte en un objetivo trivial de denegación de servicio.

En su lugar

Límite por identidad y por IP, con ventana deslizante y respuesta 429 con Retry-After. Límites más estrictos en login, recuperación de contraseña, envío de códigos y endpoints caros. Tope máximo de tamaño de página y de profundidad de consulta.

Cómo se detecta

Se lanzan ráfagas sostenidas contra los endpoints sensibles y se mira si aparece algún 429; también se prueba si cambiar la IP o un identificador de cliente reinicia el contador.

OWASP API4:2023 · CWE-770
45

Nunca pases el cuerpo entero de la petición al ORM en una actualización

Por qué falla

Si el objeto recibido se vuelca sobre la entidad, el atacante añade campos que tú nunca pusiste en el formulario —rol, saldo, verificado, empresaId— y el ORM los guarda encantado. Se llama asignación masiva y es silenciosa.

En su lugar

Selecciona explícitamente los campos actualizables por endpoint y por rol, desde un objeto validado con esquema. Nada de esparcir el cuerpo. Marca en el modelo qué atributos jamás son escribibles desde la capa HTTP.

Mal
await usuarios.update({ where: { id }, data: req.body })
Bien
const { nombre, telefono } = EsquemaPerfil.parse(req.body); await usuarios.update({ where: { id }, data: { nombre, telefono } })
Cómo se detecta

Se comparan los campos que devuelve el GET del recurso con los que acepta el PUT o el PATCH, y se envían los sobrantes para ver si se persisten.

OWASP API6:2023 · CWE-915
46

Nunca devuelvas la entidad completa de base de datos porque "el frontend ya filtra"

Por qué falla

El filtrado del frontend es cosmético: la respuesta viaja entera y se lee en las herramientas de desarrollo o con curl. Así se filtran hashes de contraseña, correos, teléfonos, notas internas y campos de otros módulos.

En su lugar

Define un objeto de salida explícito por endpoint con los campos que de verdad se publican, y adapta ese conjunto al rol de quien pregunta. Marca el esquema de respuesta en la API para que lo no declarado no salga.

Cómo se detecta

Se comparan las respuestas crudas con lo que muestra la interfaz, buscando campos de más; se revisan también las respuestas de listados y de búsqueda, que suelen ser las más generosas.

OWASP API3:2023 · CWE-213
47

Nunca aceptes un cuerpo de petición sin validar esquema, tipos y tamaño máximo

Por qué falla

Sin validación entra cualquier cosa: tipos inesperados que rompen la lógica, cadenas gigantes que agotan la memoria, objetos anidados que bloquean el bucle de eventos y campos extra que acaban en la base de datos.

En su lugar

Valida en el borde con un esquema estricto (zod o el JSON Schema de Fastify): tipos, formatos, longitudes, rangos, enumeraciones y rechazo de propiedades desconocidas. Fija un límite de tamaño de cuerpo y de profundidad, y valida también rutas, query y cabeceras.

Cómo se detecta

Se hace fuzzing con tipos cambiados, valores límite, cadenas enormes, anidamiento profundo y arrays gigantes, y se buscan 500, tiempos de respuesta disparados o datos corruptos.

OWASP API8:2023 · CWE-20

Despliegue e infraestructura

3 reglas
48

Nunca ejecutes el contenedor como root ni con más privilegios de los necesarios

Por qué falla

Si el proceso corre como root, cualquier ejecución de código que consigan es root dentro del contenedor, y desde ahí escapar al anfitrión es mucho más fácil. También permite reescribir la propia aplicación en caliente.

En su lugar

Usuario sin privilegios en la imagen, sistema de ficheros de solo lectura salvo los directorios que de verdad se escriben, capacidades del kernel recortadas, sin modo privilegiado y sin montar el socket de Docker. Imagen base mínima y actualizada.

Cómo se detecta

Se inspecciona la imagen y el manifiesto de despliegue: instrucción USER, montajes, capacidades y flags de privilegio; y se ejecuta id dentro del contenedor.

OWASP A05:2021 · CWE-250
49

Nunca expongas la base de datos, la caché o la cola directamente a internet

Por qué falla

Postgres, Redis, MongoDB y Elasticsearch abiertos al mundo se encuentran con un escaneo rutinario, y muchos vienen sin autenticación por defecto. Es el origen habitual de las fugas masivas y del ransomware de bases de datos.

En su lugar

Red privada y grupos de seguridad que solo permitan el tráfico desde los servicios que lo necesitan; acceso administrativo por túnel o bastión; autenticación obligatoria, TLS y credenciales distintas por servicio con el mínimo privilegio.

Cómo se detecta

Se escanean los puertos de los rangos públicos del proyecto y se consultan buscadores de servicios expuestos con el dominio y los rangos de la organización.

OWASP A05:2021 · CWE-1327
50

Nunca despliegues sin saber qué versión corre ni poder revertirla

Por qué falla

Cuando aparezca el incidente, la primera pregunta es qué cambió y desde cuándo; sin versión trazable no hay respuesta y el parche urgente se aplica a ciegas. Sin reversión, un despliegue malo se convierte en una caída larga.

En su lugar

Artefactos inmutables etiquetados con el hash del commit, despliegue automatizado y reproducible, reversión probada de verdad, y comprobaciones de salud y migraciones compatibles hacia atrás. La versión desplegada tiene que poder consultarse en cualquier momento.

Cómo se detecta

Se pide el identificador de versión desplegado y se contrasta con el repositorio; se revisa si existe un procedimiento de reversión y cuándo se ejecutó por última vez.

OWASP A08:2021 · CWE-494

Últimas vulnerabilidades

0 registros · ordenadas por riesgo
Aún sin datosEl boletín se rellena con la primera sincronización contra las fuentes públicas de vulnerabilidades. Vuelve en unos minutos.