# Proteger correctamente las credenciales y los archivos .env

> Proteger el archivo .env: webroot correcto, bloqueos en el servidor web, permisos de archivo estrictos, sin secretos en Git y renovación tras un incidente.

Fuente: https://phpsos.de/es/blog/env-dateien-schuetzen/  
Actualizado: 25/10/2026  
Proveedor: PHP SOS, Ayuda de emergencia para PHP y seguridad de servidores. Ayuda inmediata por WhatsApp: https://wa.me/491736494914?text=Hola%20PHP%20SOS%2C%20necesitamos%20ayuda%20con%20nuestro%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Sitio%20web%3A%20%E2%80%A6

**En resumen:** un archivo .env se protege evitando que esté siquiera en el directorio de acceso público: el webroot apunta a una subcarpeta como `public/` y la configuración está un nivel por encima. Además, el servidor web bloquea todos los archivos que empiezan por punto, el archivo solo es legible para el propietario y el proceso PHP, y nunca pertenece al repositorio Git. Tras una intrusión, todas las credenciales que contiene se consideran conocidas y se sustituyen.

## El archivo con las llaves de la casa

Los proyectos PHP modernos suelen guardar sus secretos en un archivo llamado `.env`: contraseña de la base de datos, acceso al servidor de correo, claves de API para servicios de pago y de envío, la clave con la que la aplicación firma las cookies. Frameworks como Laravel y Symfony usan este patrón, y muchos desarrollos propios lo han adoptado.

La idea es buena: configuración y código están separados, y cada entorno recibe sus propios valores. Se vuelve peligroso cuando el archivo acaba en el directorio equivocado. Si está en el webroot y el servidor web no lo bloquea, basta una sola llamada en el navegador para leer todos los accesos de golpe.

A ello se suman variantes con otros nombres: `.env.local`, `.env.production`, `config.local.php` o archivos con credenciales de servicios de almacenamiento. Lo que aquí se describe para `.env` vale para todos los archivos en los que hay secretos.

Los escáneres automatizados buscan constantemente este tipo de archivos. No es una especialidad de atacantes concretos, sino ruido de fondo de internet, similar a los escaneos masivos en busca de versiones de PHP sin parchear.

## Dónde debería estar el archivo .env

La medida más eficaz es estructural: el servidor web solo debe servir la carpeta que realmente deba ser pública. En Laravel y Symfony es la carpeta `public/`. Todo lo demás, es decir, código, configuración, logs y `.env`, está por encima y no es direccionable en absoluto mediante HTTP.

```text
/srv/www/shop/
├── .env              ← no accesible
├── config/
├── src/
├── vendor/
└── public/           ← único webroot
    ├── index.php
    └── assets/
```

En la práctica, el webroot apunta a menudo al propio directorio del proyecto, por ejemplo porque un panel de hosting lo impone o porque en una mudanza no se adaptó una ruta. Compruebe por ello, en la configuración del servidor web o en el panel, adónde apunta `root` o `DocumentRoot`.

## Proteger el archivo .env: el bloqueo en el servidor web como segunda línea

Si no puede cambiarse el webroot, o si desea una protección adicional, bloquee los archivos ocultos en el servidor web. La carpeta `.well-known` debe seguir siendo accesible, porque se usa, entre otras cosas, para la emisión de certificados.

```nginx
# Bloquear todos los archivos y carpetas que empiezan por punto, salvo .well-known
location ~ /\.(?!well-known/) {
    deny all;
    return 404;
}
```

```apache
# Bloquear los archivos que empiezan por punto
<FilesMatch "^\.">
    Require all denied
</FilesMatch>
```

Una respuesta 404 en lugar de 403 no revela que el archivo existe. Si el bloqueo funciona, compruébelo desde fuera:

```bash
# Esperado: 403 o 404, nunca 200
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.example
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.bak
```

Las dos últimas líneas no son casualidad: copias como `.env.bak` o `.env.old` surgen durante los trabajos de mantenimiento y a menudo se olvidan. Más sobre estos restos figura en [Archivos de copia de seguridad en el webroot](https://phpsos.de/es/blog/backup-dateien-im-webroot/).

## Permisos, Git y el trato con las copias

También fuera del webroot rige el principio de los mínimos permisos. Un reparto acreditado:

1. **Propietario** es el usuario de despliegue, no el proceso PHP.
2. **Grupo** es el grupo del pool de PHP-FPM, que solo puede leer; permisos `640`.
3. **Sin acceso de escritura** para el proceso PHP. Una aplicación no necesita poder modificar sus propias credenciales.
4. **Sin valores reales en el repositorio:** solo se incluye `.env.example` con marcadores de posición; el archivo real figura en `.gitignore`.
5. **Valores separados por entorno:** desarrollo, staging y producción usan accesos distintos, para que una fuga en un entorno de pruebas no abra la producción.

```bash
chown deploy:shop-php /srv/www/shop/.env
chmod 640 /srv/www/shop/.env
# Comprobar si el archivo se incluyó alguna vez en el repositorio
git log --all --oneline -- .env
```

Preste atención también a las páginas de error. En modo de depuración, los frameworks muestran en parte variables de entorno y valores de configuración; en un sistema de producción este modo debe estar desactivado. Compruébelo tras cada despliegue, porque un valor incorrecto en el propio `.env` puede volver a activarlo.

Si el último comando arroja resultados, las credenciales están en el historial de Git y se consideran expuestas, aunque hoy el archivo ya no esté. Por qué esto es especialmente delicado cuando la carpeta `.git` está en el servidor lo describe [Carpeta .git pública: una fuga de datos subestimada](https://phpsos.de/es/blog/git-ordner-oeffentlich/). Lo básico sobre permisos lo explica la guía [Permisos de archivos en el servidor web](https://phpsos.de/es/ratgeber/dateirechte-webserver/).

> **Importante:**
> Los archivos de prueba con `phpinfo()` muestran las variables de entorno. Quien pasa secretos como variables de entorno los revela a través de un archivo de prueba olvidado. Los antecedentes figuran en [Archivos phpinfo(): por qué deben desaparecer](https://phpsos.de/es/blog/phpinfo-dateien-entfernen/).

## Tras una intrusión: sustituir las credenciales

Ningún bloqueo protege frente a un atacante que ya ejecuta código en el servidor. Un proceso PHP tiene que poder leer su configuración, de modo que el código inyectado en el mismo proceso también puede hacerlo. Tras PHP2Shell rige, por tanto: si no puede descartarse una intrusión, todas las credenciales del servidor deben tratarse como conocidas.

Una sustitución ordenada abarca contraseñas de bases de datos, accesos de correo y SMTP, claves de API de servicios externos, claves de firma de la aplicación y accesos a servicios de almacenamiento. El orden y el momento deberían estar planificados: quien sustituye primero y limpia después el servidor puede estar entregando de inmediato los nuevos valores al atacante.

El daño de una fuga se limita ya de antemano si cada secreto solo puede hacer lo necesario. El usuario de base de datos del sitio web no necesita permisos sobre otras bases de datos. Las claves de API de servicios externos pueden restringirse en muchos proveedores a funciones concretas o a direcciones de remitente, por ejemplo crear envíos pero no ordenar pagos. Y cada aplicación recibe accesos propios, en lugar de que varios sitios web compartan la misma cuenta de correo o la misma clave. Así, una fuga afecta a un ámbito claramente delimitado y la sustitución sigue siendo manejable.

En entornos de contenedores, el archivo `.env` no pertenece ni a la imagen ni a un registro de acceso público. Exclúyalo mediante `.dockerignore` y pase los secretos solo en tiempo de ejecución, mediante los mecanismos de la plataforma.

A largo plazo merece la pena mirar los almacenes centrales de secretos, como los recomienda OWASP: secretos en un solo lugar, con registro de accesos, sustitución periódica y la menor validez posible.

## ¿Cómo ayuda PHP SOS en concreto?

- Comprobamos desde fuera si son accesibles archivos de configuración, copias y archivos ocultos de sus sitios web.
- Reorganizamos el webroot, los bloqueos del servidor web y los permisos de archivo de modo que los secretos ya no sean accesibles mediante HTTP.
- Revisamos repositorios y despliegues en busca de credenciales incluidas.
- Tras un incidente, planificamos y acompañamos la sustitución de todos los accesos afectados en el orden correcto.

Estos trabajos forman parte de nuestro [refuerzo de servidores web PHP](https://phpsos.de/es/leistungen/server-haertung/).


## Preguntas frecuentes

**¿Es público automáticamente un archivo .env en el webroot?**  
No automáticamente, pero sí con frecuencia. Que se pueda descargar depende de la configuración del servidor web. Muchas configuraciones predeterminadas solo protegen los archivos .htaccess, no otros archivos que empiezan por punto. Una prueba con curl lo aclara.

**¿Son las variables de entorno más seguras que un archivo .env?**  
No en principio. OWASP advierte de que las variables de entorno son legibles para todos los procesos del usuario y pueden aparecer en logs o en volcados de memoria. Pero evitan un archivo en el webroot. Mejor son los almacenes centrales de secretos, donde el entorno los admita.

**¿Qué hacer si el archivo .env era accesible?**  
Considerar conocidas todas las credenciales que contiene y sustituirlas: base de datos, correo, API, servicios de pago, claves de la aplicación. Después, comprobar en los logs de acceso si se descargó el archivo y, si hay datos personales, implicar al delegado de protección de datos.

**¿Debe estar el archivo .env en el repositorio Git?**  
No. En el repositorio solo debe haber, como mucho, una plantilla sin valores reales, por ejemplo .env.example. El archivo real se excluye mediante .gitignore y se proporciona por separado en el servidor.

**¿Puede phpinfo() revelar credenciales?**  
Sí. phpinfo() muestra, entre otras cosas, variables de entorno y variables del servidor. Si hay secretos ahí, se muestran también. Por eso, los archivos de prueba con phpinfo() no deben estar nunca en un sistema accesible.

## Fuentes

- [OWASP Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)
- [Documentación de Nginx](https://nginx.org/en/docs/)
- [Apache HTTP Server: documentación](https://httpd.apache.org/docs/)
