Руководства по безопасности

Как держать .env вне публичного веба на общем хостинге

Как закрыть .env, .git и vendor на общем хостинге для Laravel: код вне веб-корня, наружу только public/, первая помощь через .htaccess и самопроверка.

Опубликовано 2026-06-07 Обновлено 2026-10-06 9 мин чтения
На этой странице

Для всех, кто запускает приложения в стиле 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
Тело вне docroot, наружу только public/. Одно это делает .env недостижимым.

Какое исправление подходит вашей среде

«Не пускать .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, эти проверки ловят больше из того, что обычно проскальзывает. Запускайте их только для своего сервера и своих доменов.

1

Смотрите на содержимое, а не только на код статуса

Некоторые приложения отвечают на любой неизвестный URL главной страницей и кодом 200, так что один статус ничего не говорит. Выведите только начало, например curl -s https://your-domain/.env | head -c 120, и посмотрите, есть ли строки конфигурации вроде APP_KEY= или DB_PASSWORD=. Для .git/HEAD строка, начинающаяся с ref: refs/heads/, означает, что он открыт.

2

Проверяйте каждое приложение, а не одно

Повторите проверку для каждого приложения на поддомене или в подкаталоге, а не только для основного домена. Ошибки размещения обычно копируются при добавлении приложений, так что исправление одного не значит, что исправлены остальные.

3

Поищите в публичной папке на сервере

При наличии SSH ищите опасные файлы напрямую, например find ~/public_html -maxdepth 4 \( -name ".env*" -o -name ".git" -o -name "*.sql" -o -name "*.zip" \). Пустой вывод — нужный результат. Если есть только FTP, включите в FTP-клиенте показ скрытых файлов и просмотрите список.

4

Убедитесь, что .htaccess действительно работает

Положите в публичную папку безобидный тестовый файл без секретов (например, check.bak с одним словом test) и откройте его по URL. Должен вернуться 403. Если файл открывается, .htaccess не читается — проверьте тип сервера (например, Nginx) или его настройки. После проверки удалите тестовый файл.

5

Убедитесь, что режим отладки выключен

В .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, выставленный всему миру.

Читать дальше

Источники

  • Документация 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

FAQ

QЧто самое быстрое, что я могу сделать прямо сейчас?
A

Добавьте блок deny в .htaccess вашего веб-корня для .env, .git, SQL-дампов, credentials.json и т. д. Это обратимо и не влияет на обычные страницы — но это первая помощь; долговременное исправление — реструктуризация.

QПочему одного .htaccess недостаточно?
A

Чёрному списку нужно новое правило для каждого нового имени файла, поэтому что-нибудь всегда упускается. Переместите тело приложения вне веб-корня — и до .env просто не добраться, без правил для поддержки.

QПочему не просто симлинкнуть public/, чтобы выставить его?
A

В некоторых средах перенаправление docroot зарегистрированного поддомена на другое дерево через симлинк не срабатывает и возвращает 500. По опыту этого сайта, держать docroot реальной директорией и подключать (`require`) по абсолютному пути из маленького index.php (форма «bootstrap-redirect») стабильнее.

QКак проверить, виден ли .env моего сайта снаружи?
A

Для домена, которым вы владеете, откройте /.env и /.git/HEAD через curl или браузер и посмотрите и на HTTP-статус, и на содержимое. 403 или 404 — пока нормально. Ответ 200 со строками конфигурации вроде «APP_KEY=» означает, что файл открыт. Если у вас несколько приложений на поддоменах или в подкаталогах, проверьте каждое. При наличии SSH также поищите .env или .git в публичной папке на сервере.

QЧто, если сервер работает на Nginx или .htaccess не действует?
A

Nginx не читает .htaccess. В Apache .htaccess игнорируется, если AllowOverride установлен в None. В этом случае укажите веб-корень (root или DocumentRoot) на public/ Laravel в конфигурации сервера. Пример для Nginx в официальной документации Laravel также содержит правило, запрещающее файлы, начинающиеся с точки (кроме .well-known).

QЕсли мой .env был открыт, достаточно ли удалить файл?
A

Нет. Если кто-то скачал его, пока он был открыт, ключи внутри продолжают работать и после удаления файла. Перевыпустите API-ключи, OAuth-секреты, пароли БД и почты и APP_KEY и проверьте активность в каждом сервисе на незнакомые операции. Если был открыт и .git, считайте утёкшим любой секрет, который когда-либо был в прошлом коммите.