Для всех, кто запускает приложения в стиле Laravel (где наружу должна быть только public/) на общем хостинге с Apache/.htaccess. Выжато из реального инцидента (→ .env выставлен всему миру) — для защиты своего собственного развёртывания.
Взгляд этого сайта: это не «ошибка одного человека»
Ранняя культура общего хостинга клала всё прямо под public_html. Laravel (который ожидает наружу только public/) влился в это, и опасная раскладка стала де-факто стандартом — даже собственные документы вендоров кладут проекты внутрь public_html. Поэтому исправление — это процесс, а не бдительность: измените значение по умолчанию для развёртывания.
Сделайте размещение правильным (диаграмма)
✗ Опасно (тело в публичном корне)
public_html/ ├─ .env ← читаемо! ├─ .git/ vendor/ └─ public/
✓ Безопасно (тело снаружи, только public)
app-laravel/ ← вне веб-корня ├─ .env .git/ ← недостижимо └─ public/ → подключается из public_html
Какое исправление подходит вашей среде
«Не пускать .env в веб» выглядит по-разному в зависимости от типа сервера и ваших прав. Сначала найдите свою строку, затем пройдите шаги ниже.
| Среда | Постоянное исправление | Первая помощь | На что обратить внимание |
|---|---|---|---|
| Общий хостинг (с SSH) | Перенесите тело приложения из публичной папки и оставьте там только маленький index.php (Шаг 2) | Запрет в .htaccess | Если сделать саму публичную папку симлинком, в некоторых средах будет 500 |
| Общий хостинг (только FTP) | Заново загрузите тело приложения на уровень выше публичной папки (public_html и т. п.) через FTP-клиент | Запрет в .htaccess | Большинство общих хостингов разрешают запись выше публичной папки; если ваш не разрешает, спросите поддержку |
| Панель управления позволяет задать публичную папку | Укажите публичную папку прямо на app-laravel/public | Не нужна (но проверьте) | После переключения удалите копию приложения, оставшуюся в старой публичной папке |
| VPS (Apache) | Укажите DocumentRoot в VirtualHost на public/ | Правила запрета в конфигурации сервера | При AllowOverride None (по умолчанию в Apache 2.4) .htaccess не читается |
| VPS (Nginx) | Укажите root на public/ и запретите файлы, начинающиеся с точки | Правила запрета в конфигурации сервера | Nginx не читает .htaccess |
Цель везде одна: никаких .env, .git, vendor/ и storage/ внутри публичной папки. Официальная документация Laravel также предупреждает: если перенести index.php в корень проекта и отдавать сайт оттуда, многие чувствительные файлы конфигурации окажутся открыты в интернете.
Шаг 1: Первая помощь (быстро, обратимо)
Добавьте блок deny в начало .htaccess веб-корня (пример защитной конфигурации). Обычные страницы не затронуты.
# === SECURITY BLOCK ===
RedirectMatch 404 (?i)/\.(env|git)(\..*)?(/|$)
<FilesMatch "(?i)\.(sql|sql\.gz|bak|old|swp|save|orig|tgz)$|^credentials\.json$|\.bk[._]">
Require all denied
</FilesMatch>
<FilesMatch "(?i)^(phpinfo|info)\.php$">
Require all denied
</FilesMatch>
# if Laravel internals are visible at /<app>/storage/... etc.
RedirectMatch 403 (?i)^/[^/]+/(storage|bootstrap/cache|config|database|resources|routes|tests|vendor)(/|$)
# === END ===Первая помощь — не цель
Чёрный список всегда что-нибудь упускает — composer.lock или любое новое имя файла. Считайте его выигрышем времени до Шага 2.
Шаг 2: Реструктуризация (постоянное исправление)
Переместите тело приложения вне веб-корня и оставьте крошечную точку входа, которая просто загружает его.
# 1. Move the body outside the web root (same filesystem → mv is instant, atomic)
mv ~/example/public_html/app ~/example/app-laravel
# 2. Put only a minimal entry in the web root
mkdir -p ~/example/public_html/sub
cat > ~/example/public_html/sub/index.php <<'PHP'
<?php require __DIR__ . '/../../app-laravel/public/index.php';
PHP
# 3. Copy Laravel public/.htaccess, symlink static assets
# 4. Clear config cache
rm -f ~/example/app-laravel/bootstrap/cache/{config,services,packages,routes-v7}.phpУрок из эксплуатации этого сайта: bootstrap-redirect лучше симлинка
Сделать сам public_html симлинком на public/ может вернуть 500 на docroot зарегистрированного поддомена в некоторых средах (он не срабатывает). Держать docroot реальной директорией и подключать (require) по абсолютному пути из маленького index.php (форма «bootstrap-redirect») стабильнее на общем хостинге с ограниченной областью. Также opcache может запомнить неудачную загрузку и продолжать возвращать 500 — крайнее средство — сменить имя файла точки входа (entry.php).
Шаг 3: Ротируйте ключи (считайте, что их видели)
Если было любое окно выставленности, считайте ключи .env уже увиденными и замените их. Порядок: внешние API / OAuth-секреты → ключи шифрования → почта → БД. См. глоссарий .env. Переиздание OAuth-секрета может аннулировать refresh_token-ы, поэтому сначала подготовьте поток повторной авторизации.
Шаг 4: Самопроверка (сделайте привычкой)
Наконец, убедитесь, что ничто действительно не выставлено на вашем собственном сайте (только против доменов, которыми вы владеете).
# A 200 with a body means it's exposed. 403/404 is okay for now.
curl -sI https://your-domain/.env | head -1
curl -sI https://your-domain/.git/config | head -1Проверка этого при каждом развёртывании рано ловит ошибки размещения.
Как проверить свою конфигурацию (подробнее)
Помимо двух строк из Шага 4, эти проверки ловят больше из того, что обычно проскальзывает. Запускайте их только для своего сервера и своих доменов.
Смотрите на содержимое, а не только на код статуса
Некоторые приложения отвечают на любой неизвестный URL главной страницей и кодом 200, так что один статус ничего не говорит. Выведите только начало, например curl -s https://your-domain/.env | head -c 120, и посмотрите, есть ли строки конфигурации вроде APP_KEY= или DB_PASSWORD=. Для .git/HEAD строка, начинающаяся с ref: refs/heads/, означает, что он открыт.
Проверяйте каждое приложение, а не одно
Повторите проверку для каждого приложения на поддомене или в подкаталоге, а не только для основного домена. Ошибки размещения обычно копируются при добавлении приложений, так что исправление одного не значит, что исправлены остальные.
Поищите в публичной папке на сервере
При наличии SSH ищите опасные файлы напрямую, например find ~/public_html -maxdepth 4 \( -name ".env*" -o -name ".git" -o -name "*.sql" -o -name "*.zip" \). Пустой вывод — нужный результат. Если есть только FTP, включите в FTP-клиенте показ скрытых файлов и просмотрите список.
Убедитесь, что .htaccess действительно работает
Положите в публичную папку безобидный тестовый файл без секретов (например, check.bak с одним словом test) и откройте его по URL. Должен вернуться 403. Если файл открывается, .htaccess не читается — проверьте тип сервера (например, Nginx) или его настройки. После проверки удалите тестовый файл.
Убедитесь, что режим отладки выключен
В .env значение APP_ENV должно быть production, а APP_DEBUG — false. Документация Laravel указывает, что APP_DEBUG=true в продакшене грозит раскрытием чувствительных значений конфигурации пользователям. При наличии SSH то же самое показывает php artisan about --only=environment.
Частые ошибки и как их исправить
- Удалить .env, но оставить .git: по открытому
.gitможно восстановить прошлые коммиты, включая старый.envили ключи. Уберите.gitиз публичной папки тоже и перевыпустите любой секрет, который когда-либо попадал в коммит. - Оставлять в публичной папке копии вроде
.env.bakили.env.old: копия с реальными значениями так же чувствительна, как.env. Перенесите её или удалите. В.env.exampleдержите только значения-заглушки. - Загрузить весь проект ZIP-архивом и оставить архив после распаковки: в ZIP есть
.env. Удаляйте его сразу после распаковки. - Работа продакшена с
APP_DEBUG=true: страницы ошибок могут раскрыть значения конфигурации. В продакшене всегда ставьтеfalse. - Считать себя защищённым, потому что написали
.htaccess: Nginx и конфигурации сAllowOverride Noneего не читают. Подтвердите проверкой с тестовым файлом выше. - Резкая смена
APP_KEY: по документации Laravel смена ключа разлогинивает всех пользователей и делает данные, зашифрованные старым ключом, нерасшифровываемыми. В Laravel 11 и новее перед переключением поместите старый ключ вAPP_PREVIOUS_KEYS.
Реальный пример: массовый сбор открытых .env-файлов
В августе 2024 года исследовательская группа одной компании по безопасности сообщила об операции, которая автоматически собирала .env-файлы, открытые на веб-серверах, использовала облачные учётные данные из них для копирования данных, а затем требовала выкуп. Согласно отчёту, целями стали около 110 000 доменов и было собрано более 90 000 переменных окружения, из которых около 7 000 были учётными данными облачных сервисов и около 1 500 — учётными данными аккаунтов в соцсетях.
Отчёт называет причиной не уязвимость на стороне облака, а ошибки конфигурации у организаций-жертв, которые по ошибке открыли свои .env-файлы. Отсюда два вывода:
- Открытые
.env-файлы находят не случайно; программы ищут их массово - Один
.envможет распространить ущерб на другие сервисы, например облачное хранилище и почту
Не думайте, что небольшой сайт не найдут. Анонимизированный случай той же ошибки размещения — в статье .env, выставленный всему миру.
Читать дальше
- Инцидент: .env, выставленный всему миру
- Глоссарий: что такое .env
- Основы: чем опасны .env и API-ключи
- Если взломан сам провайдер: что делать, когда скомпрометирован ваш веб-хостинг (случай, который ваши собственные настройки не предотвратят)
- Проверка на вторжение: как проверить, не взломан ли ваш сайт или сервер (где смотреть, если выяснилось, что ваш .env был публичным)
Источники
- Документация Laravel, «Deployment» (направлять запросы на
public/index.php, никогда не отдавать сайт из корня проекта,APP_DEBUG=falseв продакшене): laravel.com - Документация Laravel, «Configuration» (не коммитить
.envв систему контроля версий; режим отладки): laravel.com - Документация Laravel, «Encryption» (смена
APP_KEYиAPP_PREVIOUS_KEYS): laravel.com - Apache HTTP Server 2.4, «core - AllowOverride» (
.htaccessне читается при значенииNone; по умолчаниюNone): httpd.apache.org - Apache HTTP Server 2.4, «.htaccess files» (когда их использовать и оговорки): httpd.apache.org
- OWASP, «WSTG-CONF-04 Review Old Backup and Unreferenced Files for Sensitive Information»: owasp.org
- Unit 42 (Palo Alto Networks), «Leaked Environment Variables Allow Large-Scale Extortion Operation in Cloud Environments» (15 августа 2024 г.): unit42.paloaltonetworks.com