Archivos .env expuestos y claves filtradas

Un archivo .env expuesto es un archivo de configuración —el que guarda la contraseña de tu base de datos, tus credenciales de correo y tus claves API— que tu servidor web entrega a cualquiera que lo pida por su nombre, y una carpeta .git expuesta es el mismo accidente con todo tu historial de código adjunto. Ninguno requiere que un atacante rompa nada: el archivo simplemente está en un lugar que el servidor publica, y una sola petición GET ordinaria lo descarga. Este artículo explica cómo ocurre —errores de raíz documental, alojamientos estáticos que sirven el propio repositorio, artefactos de compilación que nadie auditó—, qué hace quien lo encuentra con las claves en la primera hora, y por qué borrar el archivo después es la parte más pequeña de la respuesta. Para comprobar tu propio sitio ahora, ejecuta el escáner de secretos filtrados y lee con tus resultados.

Cómo un archivo privado se vuelve público: la raíz documental

Todo servidor web tiene una raíz documental: el directorio cuyo contenido es público por definición. Todo lo que se coloca dentro —a propósito o por accidente— puede pedirse por URL, lo enlace o no alguna página. El accidente clásico es un proyecto desplegado copiando todo el directorio de trabajo al servidor: el código de la aplicación, que sí pertenece ahí, llega junto al archivo .env que tiene al lado, la carpeta .git que lleva dentro, las copias de editor, el volcado de base de datos que alguien hizo antes de la migración y el archivo de la versión del mes pasado. El servidor no distingue unas cosas de otras. Sirve archivos; los privados también son archivos.

Las convenciones de los frameworks hacen fácil caer en la trampa. Un archivo .env debería estar un nivel por encima de la raíz documental, o fuera por completo del árbol desplegado, y la aplicación debería leerlo del disco, nunca a través del servidor web. Pero los servidores de desarrollo sirven gustosos la raíz del proyecto, los despliegues pequeños lo arrastran todo a public_html, y una configuración copiada de un tutorial puede apuntar la raíz documental al propio proyecto. Nada se rompe visiblemente cuando esto ocurre, y ese es exactamente el problema: el sitio funciona perfectamente, el archivo es público, y el primero en notarlo es quien pide /.env, una petición que los escáneres automatizados hacen contra cada sitio de internet, sin parar.

Alojamientos estáticos, repositorios servidos y artefactos de compilación

El alojamiento estático retira la configuración del servidor de la ecuación y la sustituye por una regla más simple: lo que esté en el directorio de publicación se publica. Despliega un repositorio en un alojamiento estático con el directorio de salida puesto en la raíz del proyecto y el alojamiento servirá fielmente el repositorio, .git incluido si se copió a la salida. Un .git expuesto es peor de lo que parece: el archivo HEAD y la configuración prueban la estructura, los archivos de objetos contienen cada blob jamás confirmado, y con las herramientas Git ordinarias puede reconstruirse todo el historial, incluidos los secretos confirmados una vez y «borrados» en una confirmación posterior, lo que los retira del árbol de trabajo y de ningún otro sitio.

Los artefactos de compilación crean una segunda clase de exposición, más silenciosa. Los empaquetadores de JavaScript incrustan lo que el código referencia, así que una clave API leída de una variable de entorno en tiempo de compilación puede acabar como una cadena literal dentro del paquete enviado: público por diseño, legible por cada visitante, y nunca señalado por ninguna comprobación de permisos, porque ese archivo sí debía ser público. Los mapas de fuentes lo agravan enviando el código original junto al paquete. Nada de esto necesita un servidor mal configurado; solo necesita una compilación que nunca se inspeccionó desde fuera.

El tercer patrón es el resto olvidado: scripts de despliegue que descomprimen un archivo en la raíz web y dejan el archivo ahí, complementos de copia que escriben volcados en una carpeta accesible por web, un config.php.bak guardado por un editor, que muchos servidores sirven como texto plano precisamente porque la extensión ya no coincide con el manejador PHP. Cada resto es una copia completa de algo privado, en una dirección predecible.

Qué hacen los atacantes en la primera hora

El descubrimiento es automatizado y constante. Los escáneres enumeran las rutas conocidas —archivos env, interiores de Git, nombres de configuración, extensiones de copia— en rangos enormes de direcciones, y una exposición fresca suele encontrarse en horas, a menudo antes. Lo que llega a tu servidor es una petición GET sin nada de particular que devuelve 200; no hay alarma, porque desde la perspectiva del servidor no ocurrió nada inusual. Asume, por tanto, que cualquier archivo que fue público un día fue descargado, y que la carga de la prueba corre en sentido contrario.

La selección también es automatizada. Un .env descargado se analiza en segundos: las credenciales de base de datos se prueban contra cualquier puerto alcanzable, las claves de nube se validan con una llamada inofensiva que dice si están vivas, las credenciales de correo se prueban para enviar, y las claves de pago se comprueban por su modo: una clave de pago en modo real y unas credenciales de producción son el premio gordo, porque cada una se convierte directamente en dinero o en datos. Las claves válidas se usan, se comercian o se guardan rápido, porque sus dueños pueden rotarlas en cualquier momento; la primera hora es cuando una clave expuesta vale más, y se gasta con eficiencia.

Por qué borrar el archivo no basta

Borrar el archivo detiene las descargas nuevas. No hace nada con las copias que ya existen: la de quien lo encontró, obviamente, pero también las que hiciste tú: la respuesta guardada por tu CDN o un proxy corporativo, la instantánea en el rastreo de un buscador o un servicio de archivo, el paquete que un navegador ya descargó. Hasta que cada caché se purga y expira, el archivo «borrado» puede seguir siendo servido por infraestructura que controlas pero olvidaste limpiar, y la versión fuera de tu control está fuera de él para siempre.

Por eso la primera respuesta a una exposición confirmada es rotar, no borrar: revoca la credencial expuesta y emite una nueva, para que cada copia del mundo pierda su valor en el mismo momento. Rotar tiene un orden —emitir el reemplazo, desplegarlo, verificar que el servicio funciona y luego revocar la credencial antigua— y se extiende a todo lo que el material expuesto puede abrir: una exposición de .git significa rotar cada secreto que apareció alguna vez en el historial, no solo los actuales, porque lo divulgado es el historial. Después, lee tus registros de acceso para las rutas expuestas: te dicen si fuiste el primero en saberlo, y las obtenciones anteriores convierten un incidente de configuración en una investigación de brecha con una lista muy distinta.

La lista de sondeo, explicada — y la lista de prevención

Un escáner como el escáner de secretos filtrados solo puede pedir archivos por su nombre, así que su lista es el conjunto conocido: /.env y sus variantes de entorno, /.git/HEAD y /.git/config, nombres familiares de configuración, extensiones de copia y de archivo, y nombres de archivos de claves, más los propios scripts de la página, buscados en forma de cadenas con forma de clave. El límite honesto corta en ambos sentidos: un escáner solo encuentra lo que pide, y por eso confirma un hallazgo por el contenido, no por el código de estado. Un 200 que devuelve tu portada es una ruta comodín, no una filtración, y cualquier informe que lo cuente como tal se está inflando. El riesgo de cadena de suministro relacionado —los scripts que cargas deliberadamente de otros— se trata en scripts de terceros y riesgo de cadena de suministro.

Prevenir es sobre todo colocación y una capa de rechazo. Mantén los secretos fuera de la raíz documental y fuera de la salida de publicación del repositorio; haz que el servidor rechace los nombres peligrosos aunque aterricen ahí —reglas de denegación para archivos con punto, extensiones env y de configuración, archivos comprimidos y material de claves, probadas pidiéndolos—; retira .git de los artefactos de despliegue o bloquéalo directamente. En la canalización, añade escaneo de secretos en CI: en las confirmaciones, para que una clave se detenga antes de enviarse, y en el artefacto construido, para que una clave empaquetada se atrape en la salida y no la atrape un extraño. Por último, escanea tú mismo con regularidad y tras cada cambio de despliegue: las exposiciones de este artículo son todas silenciosas, y la única manera de enterarte de la tuya antes que otro es hacer a tu propio servidor las mismas preguntas que hacen los escáneres.

Fuentes y lecturas adicionales

Preguntas frecuentes

Borré mi archivo .env expuesto. ¿Ya estoy a salvo?

Todavía no. Borrar detiene las descargas nuevas, pero puede haber copias en manos de quien lo encontró, en cachés de CDN y proxies, y en rastreos y archivos. Rota cada credencial que contenía el archivo —revoca las claves antiguas y emite unas nuevas—, purga tus cachés y revisa los registros de acceso de la ruta. Solo rotar vuelve inútiles las copias.

Mi sitio devuelve 200 para /.env. ¿Está expuesto el archivo?

No necesariamente. Muchos sitios responden a cada ruta desconocida con un 200 y su portada o su aplicación: un respaldo comodín. Lo que importa es el contenido: si el cuerpo de la respuesta es tu página, nada está expuesto; si son líneas clave–valor con valores reales, sí lo está. Un escáner fiable confirma por el contenido y lista los 200 desnudos aparte como sin confirmar.

¿Qué debería ejecutarse en CI para que no vuelva a ocurrir?

Dos comprobaciones en dos momentos: un escáner de secretos en la confirmación, que impide que una clave entre en el repositorio, y una comprobación del artefacto construido y del directorio de salida desplegado, que atrapa lo que la compilación incrustó y lo que el despliegue copió. Añade reglas de denegación del servidor para archivos con punto y material de claves como última capa, y vuelve a ejecutar una sonda externa tras los cambios de despliegue.