"Seguridad en Replit" son dos cosas que conviene separar. La primera es el Repl y su despliegue: dónde viven los secretos, quién ve tus archivos, qué URL es pública y qué puerto está expuesto. La segunda es el código que Replit Agent genera, normalmente un servidor Express con React y una base de datos Postgres, que trae los fallos clásicos de un backend escrito deprisa. Esta guía cubre las dos, con un bloque para el archivo que el Agent lee en cada petición, un prompt de revisión antes de desplegar y las comprobaciones desde fuera.
Parte 1: proteger tu Repl y tu despliegue
Secretos: la pestaña Secrets, no el código
Replit tiene un gestor de secretos que guarda las variables cifradas y las expone al proceso como variables de entorno, sin que aparezcan en ningún archivo. Es el único sitio correcto para claves de API, la cadena de conexión de la base de datos y el secreto de sesión. El error habitual es que el Agent, o tú con prisa, las escriba en un .env, en config.json o directamente en el código. Esos archivos forman parte del Repl: se ven en un Repl público, se copian al hacer fork y se despliegan.
Repls públicos y la URL de desarrollo
Un Repl público muestra todos sus archivos a cualquiera, incluidos los que olvidaste: copias de seguridad, un .env antiguo, exportaciones de datos. Antes de hacerlo público, revisa el árbol completo. Y mientras el Repl está en ejecución, la URL de desarrollo .replit.dev responde a quien la tenga; no es un entorno privado, es un servidor en internet con tus datos de prueba.
Despliegues
El despliegue publica la app en un subdominio .replit.app o en tu dominio, con TLS automático. Las variables de entorno del despliegue se configuran aparte de las del desarrollo, así que comprueba que las de producción no apuntan a la base de datos de pruebas. Solo el puerto que declaras queda expuesto, pero todo lo que sirve ese puerto es público: rutas de API, archivos estáticos y lo que el servidor devuelva por error. Replit no añade cabeceras de seguridad; las pone tu servidor.
Base de datos y autenticación integradas
La base de datos Postgres integrada entrega su cadena de conexión como secreto; nunca la copies a un archivo. Si usas Replit Auth, la sesión la gestiona Replit y tu código recibe el usuario ya identificado, pero cada ruta sigue teniendo que comprobar que hay sesión y que ese usuario puede hacer lo que pide.
Parte 2: los fallos que el Agent mete en tu código
- Secretos en archivos. La clave de OpenAI o la cadena de Postgres en
.env, en una constante o en unconfig.jsonque se sirve como estático. - Rutas de API sin autenticación. El panel comprueba la sesión en React;
/api/usersresponde a cualquiera. - Carpetas estáticas demasiado amplias.
express.staticapuntando a la raíz del proyecto sirvepackage.json,.replit, el código del servidor y cualquier archivo olvidado. Y sinindex.html, algunos servidores listan el directorio. - Rutas de depuración y semilla.
/api/debug,/api/seed,/api/resetcreadas para probar y desplegadas. - CORS abierto con credenciales.
cors()sin opciones más cookies de sesión: cualquier web actúa en nombre de tus usuarios. - Consultas SQL por concatenación. Una cadena construida con lo que llega del formulario en lugar de parámetros.
- Subidas de archivos a disco servidas tal cual. Cualquier usuario sube un archivo y cualquier visitante lo descarga por su nombre.
- Sin límite de peticiones ni cabeceras. Un endpoint que llama a un modelo de pago sin tope, y ninguna cabecera de seguridad.
Un bloque para que el Agent lo aplique siempre
Replit Agent lee el archivo replit.md de tu proyecto como contexto permanente. Añade esta sección:
## Reglas de seguridad
- Las claves, la cadena de la base de datos y el secreto de sesión se leen de process.env y viven en la pestaña Secrets. Nunca en .env, config.json ni en el código.
- Toda ruta de API que lea o escriba datos comprueba la sesión y los permisos del usuario en el servidor. Las comprobaciones en React no cuentan.
- express.static solo sirve la carpeta de build del frontend o public/, nunca la raíz del proyecto.
- Sin rutas de depuración, semilla o reset en producción.
- CORS con una lista explícita de orígenes; nunca el comodín con credenciales.
- Todas las consultas SQL usan parámetros. Toda entrada de formulario se valida en el servidor.
- Los archivos subidos se guardan fuera de la carpeta pública y se sirven solo tras comprobar permisos.
- Cabeceras de seguridad con helmet y límite de peticiones en las rutas con coste.
Y las cabeceras en Express, en dos líneas:
import helmet from 'helmet';
app.use(helmet());
app.use(helmet.hsts({ maxAge: 63072000, includeSubDomains: true, preload: true }));
Un prompt de revisión antes de desplegar
Revisa la seguridad de este proyecto y no cambies nada todavía. Responde con una lista:
1. Claves, cadenas de conexión o secretos escritos en archivos o en el código, con archivo y línea.
2. Rutas de API que lean o escriban datos sin comprobar la sesión, y rutas de depuración, semilla o reset.
3. Carpetas servidas como estáticas y qué archivos exponen.
4. Configuración de CORS y de cookies de sesión.
5. Consultas SQL construidas por concatenación y entradas sin validar.
6. Dónde se guardan los archivos subidos y cómo se sirven.
7. Si existen cabeceras de seguridad y límite de peticiones.
Para cada punto, propón el cambio exacto y espera mi confirmación.
Comprueba el resultado desde fuera
curl -sI https://tudominio.com | grep -iE "strict-transport|x-frame|x-content-type|content-security"
for p in .env .replit package.json config.json .git/config server.js uploads/; do printf "%-14s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://tudominio.com/$p"; done
curl -s -o /dev/null -w "%{http_code}\n" https://tudominio.com/api/users
Lo esperado: cabeceras presentes, 404 en toda la lista de archivos y un 401 o 403 en la ruta de API sin sesión. Un 200 en package.json o .replit significa que estás sirviendo la raíz del proyecto. Scanaris hace estas comprobaciones y unas cuarenta más en 30 segundos: busca archivos expuestos y listados de directorio, endpoints que respondan sin sesión, secretos en el JavaScript, cabeceras, CORS, cookies y TLS, y te da un fix prompt por cada fallo para pegar en el chat del Agent. Y puede volver a escanear cada día, porque una regeneración puede devolver un fallo que ya habías arreglado.
Preguntas frecuentes
¿Los secretos de la pestaña Secrets se ven en un Repl público?
No, los secretos no se muestran ni se copian al hacer fork. Lo que sí se ve es todo lo demás: archivos, código y cualquier clave que esté escrita en ellos en lugar de en Secrets.
¿La URL de desarrollo es privada?
No. Mientras el Repl corre, responde a cualquiera que tenga el enlace. No la uses con datos reales ni la compartas pensando que solo tú la ves.
¿Replit pone cabeceras de seguridad en los despliegues?
No. Da TLS y el dominio, pero las cabeceras las envía tu servidor. Con Express, helmet las añade en dos líneas; con otros frameworks, su equivalente.