Checklist de seguridad para publicar un bot, sitio o API

Antes de publicar, saca los secretos del código, usa HTTPS, actualiza las dependencias, valida las entradas, limita los permisos y revisa los logs. Si se filtró un secreto, cámbialo enseguida.

Actualizado el 8 min de lectura

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

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:

  1. Secretos fuera del código y de Git.
  2. Dependencias actualizadas.
  3. HTTPS y cabeceras de seguridad.
  4. Autenticación, autorización y validación de entrada.
  5. Permisos mínimos para tokens, claves y usuarios de la base de datos.
  6. Logs sin datos sensibles y errores que no filtran detalles.
  7. 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).

InternetRedHTTPS, cabeceras de seguridad y WAF3Código y dependenciasautenticación, validación y npm audit24Permisosmínimo privilegio en tokens, claves y base5Secretosvariables de entorno, nunca en Git17Logssin datossensibles6
Los números son los puntos del checklist: cada uno protege una capa, y los logs registran lo que pasa por todas.

¿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:

bash
echo ".env" >> .gitignore
git ls-files .env                # no debe imprimir nada
git log --all --oneline -- .env  # no debe imprimir nada

El 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 ENV o ARG. 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.

bash
npm audit --omit=dev      # informe solo de las dependencias de producción
npm audit fix             # aplica las correcciones compatibles

npm 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.

bash
npm install express@5 helmet
js
import 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.

  1. 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.
  2. Genera uno nuevo y actualiza la variable de entorno en el hosting.
  3. Reinicia la aplicación para que lea el valor nuevo.
  4. Investiga los logs del servicio en busca de uso sospechoso durante el intervalo en que el secreto estuvo expuesto.
  5. 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:

bash
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 audit durante 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.

  1. Secretos (punto 1): variables de entorno por aplicación, en el panel, la API, la CLI o VS Code. El panel importa tu .env local, 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.
  2. Dependencias (punto 2): al iniciar, cuando no hay node_modules, la plataforma ejecuta npm install con NODE_ENV=production, así que las devDependencies no llegan a producción y npm audit --omit=dev cubre exactamente lo que corre. Incluye el package-lock.json para que se instalen las versiones que probaste.
  3. HTTPS (punto 3): toda aplicación web recibe un subdominio *.squareweb.app con HTTPS. El certificado se renueva solo, el HTTP redirige a HTTPS y las cabeceras de Helmet, como HSTS, pasan intactas.
  4. 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.
  5. 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

Más información

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.

Siguiente artículoCómo proteger tu web contra DDoSPara protegerte de un DDoS, combina mitigación en el borde (CDN y WAF) con límite de peticiones, timeouts y caché en la aplicación. Guía paso a paso con código.

Más en Seguridad

Ver los 10 artículos