Pensar como un atacante
Una API publicada recibe peticiones de cualquiera. Asume que toda entrada es hostil hasta que la valides. El proyecto OWASP mantiene la lista de los riesgos más habituales; estos son los básicos que debes conocer.
Inyección
Ocurre cuando datos del usuario se interpretan como código/comandos. El caso clásico es la inyección SQL:
// MAL: el usuario controla parte de la consulta.
db.query("SELECT * FROM users WHERE name = '" + nombre + "'");
// Si nombre = "' OR '1'='1", la condición siempre es verdadera.
// BIEN: consultas parametrizadas. El motor trata el valor como dato, no código.
db.query("SELECT * FROM users WHERE name = $1", [nombre]);
La defensa general: nunca construyas comandos concatenando texto del usuario; usa parámetros, prepared statements o un ORM.
XSS (Cross-Site Scripting)
Si guardas un comentario como <script>robarCookies()</script> y luego lo
pintas tal cual en el HTML, ese script se ejecuta en el navegador de otros
usuarios. La defensa es escapar/sanear la salida (convertir < en <,
etc.) y, en el cliente, evitar innerHTML con datos no confiables.
Sanear y validar la entrada
Valida tipos, longitudes y formatos en el servidor (nunca confíes solo en la validación del navegador). Rechaza lo que no encaje y normaliza lo que aceptes.
CORS
El navegador, por la política del mismo origen, bloquea por defecto que una
web en https://miapp.com llame por fetch a tu API en https://api.otra.com.
CORS (Cross-Origin Resource Sharing) es el mecanismo por el que tu servidor
autoriza explícitamente qué orígenes pueden llamarlo, mediante cabeceras como
Access-Control-Allow-Origin. No es una protección contra el atacante: es un
permiso que tu API concede a webs de otro origen.
Rate limiting
Limitar cuántas peticiones acepta un cliente por unidad de tiempo. Frena la fuerza bruta contra el login y los abusos/DoS. Quien se pasa recibe un 429 Too Many Requests.
Secretos en variables de entorno
Las claves (secreto JWT, contraseñas de BD, claves de API) no se escriben en el código ni se suben al repositorio. Van en variables de entorno y se leen en ejecución:
const SECRETO = process.env.JWT_SECRET;
HTTPS siempre
Sin HTTPS, los tokens y contraseñas viajan en texto plano y cualquiera en la red puede leerlos. En producción, todo el tráfico va cifrado con TLS.
Resumen de defensas
| Riesgo | Defensa |
|---|---|
| Inyección | Consultas parametrizadas, ORM |
| XSS | Escapar/sanear la salida |
| Robo de credenciales en tránsito | HTTPS/TLS |
| Fuerza bruta | Rate limiting + hashing lento |
| Secretos filtrados | Variables de entorno |
| Llamadas entre orígenes | CORS configurado a propósito |