Перейти к содержимому
>_ITDITDПлатформа веб-безопасности

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

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

Сделайте .env, .git и vendor недостижимыми, когда вы развёртываете приложения в стиле Laravel на общем хостинге. Настоящее исправление: тело приложения вне веб-корня, наружу только public/. Диаграмма размещения, первая помощь через .htaccess, реструктуризация, ловушка bootstrap-redirect этого сайта и самопроверка.

Опубликовано 2026-06-07 Обновлено 2026-06-07 3 мин чтения

Для всех, кто запускает приложения в стиле 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 недостижимым.

Шаг 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

Проверка этого при каждом развёртывании рано ловит ошибки размещения.

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

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») стабильнее.