Objetivos de aprendizaje
Después de leer este artículo podrás:
- Revisar los puntos de seguridad que importan antes de poner en línea un bot, sitio o API
- Aplicar cabeceras de seguridad y validación de entrada en una API Express
- Comprobar con comandos sencillos si algún secreto acabó en Git
En esta página
- ¿Cómo mantener los secretos fuera del código?
- ¿Cómo actualizar las dependencias con seguridad?
- ¿Cómo configurar HTTPS y las cabeceras de seguridad?
- ¿Cómo validar las entradas y limitar el acceso?
- ¿Qué registrar en los logs y qué no registrar nunca?
- ¿Qué hacer cuando se filtra un secreto?
- ¿Cómo comprobar que todo funciona y qué errores evitar?
- Cómo hacerlo en Square Cloud
- Preguntas frecuentes
Buena parte de los incidentes en bots, sitios y APIs pequeños vienen de fallos básicos: un token en el repositorio, una dependencia vieja, un endpoint sin autenticación. No hace falta ser experto en seguridad para cerrar esas puertas. Basta con repasar una lista antes del deploy y repetirla de vez en cuando.
Este checklist cubre lo que un desarrollador que trabaja solo puede hacer en una tarde. Los ejemplos usan una versión reciente de Node.js y Express 5, pero los puntos valen para cualquier lenguaje. Si tu duda es solo sobre el token de Discord, lee cómo proteger el token del bot.
El checklist, en orden:
- Secretos fuera del código y de Git.
- Dependencias actualizadas.
- HTTPS y cabeceras de seguridad.
- Autenticación, autorización y validación de entrada.
- Permisos mínimos para tokens, claves y usuarios de la base de datos.
- Logs sin datos sensibles y errores que no filtran detalles.
- Un plan para cuando se filtre un secreto.
Estos puntos cubren buena parte de las categorías del OWASP Top 10:2025, como el control de acceso roto (A01), la configuración de seguridad incorrecta (A02), los fallos en la cadena de suministro de software (A03) y los fallos de autenticación (A07).
¿Cómo mantener los secretos fuera del código?#
El token del bot, la contraseña de la base de datos, la clave de API y el secreto del webhook entran en la aplicación por variables de entorno, nunca escritos en el código. Quien tiene acceso al repositorio no debería tener acceso a producción solo por eso. Si el concepto es nuevo para ti, mira qué son las variables de entorno.
Para el desarrollo local, usa un archivo .env y añádelo al .gitignore antes del primer commit:
echo ".env" >> .gitignore
git ls-files .env # no debe imprimir nada
git log --all --oneline -- .env # no debe imprimir nadaEl primer comando confirma que el archivo no está versionado. El segundo muestra si alguna vez se hizo commit de él, en cualquier rama. Si aparece algún commit, el secreto está en el historial. Revócalo y genera otro. La guía de secretos de OWASP deja claro que reescribir el historial de Git tiene efectos secundarios y no sustituye la rotación.
Tres cuidados más:
- No metas secretos en imágenes Docker con
ENVoARG. Inyéctalos al ejecutar. - Usa un secreto distinto para cada entorno (pruebas y producción).
- Activa el secret scanning de GitHub, que busca credenciales expuestas en todo el historial, en todas las ramas, y genera una alerta. Se ejecuta automáticamente en los repositorios públicos.
¿Cómo actualizar las dependencias con seguridad?#
Tu código es pequeño, pero node_modules no. Cada paquete es código de terceros que corre con los permisos de tu aplicación, y aparecen vulnerabilidades nuevas aunque no toques nada.
npm audit --omit=dev # informe solo de las dependencias de producción
npm audit fix # aplica las correcciones compatiblesnpm audit envía la lista de dependencias al registro y devuelve las vulnerabilidades conocidas, y termina con un código distinto de cero si encuentra alguna. Las correcciones que exigen cambiar el rango de versión piden --force, y eso puede romper el código, así que ejecuta tus pruebas después.
Para no depender de la memoria, activa Dependabot security updates en GitHub: abre pull requests con la versión corregida mínima cuando sale una alerta. Mantén el package-lock.json en el repositorio para que el deploy instale exactamente las versiones que probaste, e instala con npm ci.
Desconfía de los paquetes con un nombre parecido al de uno popular y de los paquetes nuevos con pocas descargas: así entran los ataques a la cadena de suministro.
¿Cómo configurar HTTPS y las cabeceras de seguridad?#
Todo el tráfico que llega de fuera debe usar HTTPS. Sin él, las contraseñas y los tokens viajan en texto plano y cualquier red en el camino puede leer o alterar la respuesta. En un hosting con certificado automático, no emites nada a mano. Mira cómo funciona SSL/TLS.
Después de HTTPS, las cabeceras. El paquete helmet define varias de una vez, entre ellas Strict-Transport-Security, X-Content-Type-Options: nosniff, Content-Security-Policy y Referrer-Policy, y elimina X-Powered-By, que revela que usas Express.
npm install express@5 helmetimport express from "express";
import helmet from "helmet";
const { API_TOKEN } = process.env;
if (!API_TOKEN) {
console.error("Falta API_TOKEN");
process.exit(1);
}
const app = express();
app.use(helmet());
app.use(express.json({ limit: "100kb" }));
// autenticación antes de cualquier ruta privada
app.use((req, res, next) => {
const token = (req.headers.authorization ?? "").replace(/^Bearer /, "");
if (token !== API_TOKEN) return res.status(401).json({ error: "Unauthorized" });
next();
});
app.post("/items", (req, res) => {
const { name } = req.body ?? {};
if (typeof name !== "string" || name.length === 0 || name.length > 100) {
return res.status(400).json({ error: "name debe tener entre 1 y 100 caracteres" });
}
res.status(201).json({ name });
});
// error genérico: el detalle va al log, no al cliente
app.use((err, _req, res, _next) => {
console.error(err);
res.status(500).json({ error: "Internal Server Error" });
});
app.listen(Number(process.env.PORT ?? 3000));Strict-Transport-Security solo tiene efecto en respuestas HTTPS: el navegador recuerda, durante los segundos indicados en max-age, que al sitio solo se accede por HTTPS. El valor por defecto de Helmet es un año con includeSubDomains. La Content-Security-Policy por defecto es restrictiva, así que un sitio con scripts e imágenes de otros dominios tiene que ajustarla. Para una API que solo responde JSON, rara vez molesta.
Para el resto de la API (rate limit, CORS, comparación del token en tiempo constante), sigue el checklist de producción de la API.
¿Cómo validar las entradas y limitar el acceso?#
Trata todo lo que viene de fuera como hostil: el cuerpo, la query string, las cabeceras, el nombre del archivo subido e incluso los mensajes que los usuarios envían a tu bot. Comprueba el tipo, el tamaño y el formato, y usa consultas parametrizadas en la base de datos en lugar de construir SQL concatenando texto.
Autenticar no basta: el control de acceso roto encabeza el OWASP Top 10:2025. Si GET /orders/42 devuelve el pedido 42 a cualquier usuario autenticado, cualquiera cambia el número y lee pedidos ajenos. Toda ruta que accede a un recurso debe comprobar que ese usuario es su dueño.
En los bots vale lo mismo para los comandos: un comando que borra mensajes o cambia la configuración tiene que comprobar el rol o el permiso de quien lo llamó, no confiar en que el comando está escondido.
Mínimo privilegio. Cada credencial debe poder hacer solo lo que exige su uso. La guía de secretos de OWASP recomienda dar a cada secreto el mínimo de privilegios y limitar las credenciales de CI/CD a los secretos que el job usa de verdad.
- Bot: pide solo los permisos y los intents que necesita, nunca "Administrador" por comodidad.
- Base de datos: un usuario de la aplicación sin permiso para crear ni borrar tablas, separado del usuario de migraciones.
- Claves de API: una clave por servicio, para revocar una sin tumbar las demás.
- Personas: quien solo lee logs no necesita acceso para hacer deploy.
¿Qué registrar en los logs y qué no registrar nunca?#
Registra los eventos que ayudan a investigar: inicios de sesión fallidos, respuestas 401 y 403, errores 500 y acciones administrativas. Los fallos de registro y alertas aparecen como categoría propia en el OWASP Top 10:2025, porque sin registro no te das cuenta del ataque.
Nunca registres contraseñas, tokens, números de tarjeta ni el cuerpo entero de la petición. Y no devuelvas el stack trace al cliente: revela rutas, bibliotecas y versiones. El ejemplo de arriba registra el error completo en el servidor y responde solo "Internal Server Error".
¿Qué hacer cuando se filtra un secreto?#
Actúa en este orden: revocar, cambiar, investigar.
- Revoca el secreto en el servicio de origen (portal de Discord, panel de la base de datos, proveedor de la API). Borrar el commit no sirve.
- Genera uno nuevo y actualiza la variable de entorno en el hosting.
- Reinicia la aplicación para que lea el valor nuevo.
- Investiga los logs del servicio en busca de uso sospechoso durante el intervalo en que el secreto estuvo expuesto.
- Corrige la causa:
.gitignore, secret scanning y una revisión de quién tenía acceso.
¿Cómo comprobar que todo funciona y qué errores evitar?#
Con la aplicación en línea, comprueba las cabeceras y la autenticación:
curl -sI https://tu-app.ejemplo.com | grep -iE "strict-transport|x-content-type|x-powered-by"
curl -i https://tu-app.ejemplo.com/items -X POST -H "Content-Type: application/json" -d '{"name":"a"}'Lo esperado: strict-transport-security y x-content-type-options presentes, x-powered-by ausente y la segunda llamada devolviendo 401. Ejecuta también npm audit --omit=dev y git log --all --oneline -- .env.
Los errores que más aparecen:
- Hacer commit del
.env"solo una vez": el historial lo guarda para siempre. - El token del bot en una captura o en un log de error pegado en un foro o un servidor de soporte.
- CORS con
*en una API autenticada. - Un panel de administración sin autenticación, pensando que basta con que la URL sea desconocida.
- Ignorar el aviso de
npm auditdurante meses. - La misma contraseña de base de datos en pruebas y producción.
Cómo hacerlo en Square Cloud#
Square Cloud es hosting self-service: tú haces el deploy y defines la configuración, así que el checklist sigue siendo tuyo, pero varios puntos ya tienen dónde apoyarse.
- Secretos (punto 1): variables de entorno por aplicación, en el panel, la API, la CLI o VS Code. El panel importa tu
.envlocal, y el cambio se aplica en el siguiente reinicio, sin rebuild. Para un deploy con GitHub Action, la única credencial es una API key guardada como secret del repositorio. - Dependencias (punto 2): al iniciar, cuando no hay
node_modules, la plataforma ejecutanpm installconNODE_ENV=production, así que las devDependencies no llegan a producción ynpm audit --omit=devcubre exactamente lo que corre. Incluye elpackage-lock.jsonpara que se instalen las versiones que probaste. - HTTPS (punto 3): toda aplicación web recibe un subdominio
*.squareweb.appcon HTTPS. El certificado se renueva solo, el HTTP redirige a HTTPS y las cabeceras de Helmet, como HSTS, pasan intactas. - Red y aislamiento: todos los planes incluyen WAF y protección DDoS L3/L4/L7, y las aplicaciones corren en contenedores aislados, sin acceso root; una no lee las variables ni los archivos de otra, aunque estén en la misma cuenta. Esas capas no sustituyen la autenticación, la validación ni el rate limit en tu código.
- Filtraciones (punto 7): cambia la variable y reinicia la aplicación desde el panel, la CLI o la API, siguiendo el arranque en los logs en tiempo real.
Después del deploy, ejecuta los dos curl de la sección de comprobación contra tu subdominio squareweb.app. Mira los detalles en red y protección y en variables de entorno.
Infraestructura
Mitigación de DDoS L3/L4/L7 y WAF en todos los planes
Preguntas frecuentes
¿Cuál es el error de seguridad más común al publicar un bot o una API?
Dejar un token, una contraseña o una clave de API dentro del código o del repositorio. En un repositorio público cualquiera puede copiarlo, así que trata el secreto como filtrado y genera otro.
¿Borrar el archivo .env del repositorio lo arregla si ya hice commit?
No. El secreto sigue en el historial de Git. Revoca y genera una credencial nueva; limpiar el historial ayuda, pero no sustituye el cambio.
¿Necesito HTTPS para un bot que solo habla con Discord o Telegram?
El bot en sí solo hace conexiones salientes, que ya usan TLS. HTTPS pasa a ser obligatorio si recibe tráfico, como un webhook, un panel web o una API.
¿Cada cuánto debo ejecutar npm audit?
En cada deploy y de forma automática en el repositorio, por ejemplo con Dependabot. Se publican vulnerabilidades nuevas todo el tiempo, aunque no toques el código.
Más en Seguridad
Ver los 10 artículos- ¿Qué es un ataque de fuerza bruta y cómo protegerte?Un ataque de fuerza bruta prueba contraseñas en masa hasta acertar una. Mira los tipos (spraying, credential stuffing), cómo funcionan y las defensas con código.
- Hackearon mi aplicación: qué hacer ahoraSi hackearon tu aplicación, contén el acceso, cambia todos los secretos y conserva los logs antes de limpiar. El orden correcto, cómo hallar la causa y cómo volver.
- Clave de API expuesta en GitHub: cómo revocarla y prevenirlo¿Subiste una clave de API a GitHub? Revócala primero, luego limpia el historial y cambia el valor. Mira el paso a paso y cómo evitar que se repita.
- ¿Se filtró el token de tu bot? Qué hacer ahoraSi se filtró el token de tu bot, restablécelo en el Portal de Discord (o con /token en BotFather) antes que nada. Mira el orden correcto y cómo revisar el daño.