対策・学習15分で読める

レンタルサーバーで .env を公開しないための配置と設定

共用レンタルサーバーにLaravel等を置くとき、.env・.git・vendorを外から読めなくする方法です。アプリ本体を公開ルートの外に置く構造、.htaccessでの応急処置、恒久対策と自己点検を解説します。

本文を読む
公開日
更新日
目次

対象:共用レンタルサーバー(Apache/.htaccess が使える環境)に Laravel など「public/ だけ公開する前提」のアプリを置いている人。実際の事故(→ .env全公開の話)から抽出した、自分の環境を守るための手順です。

当サイトの視点:これは個人のミスではない

レンタルサーバー黎明期の「public_html 直下に全部置く」文化に、public/ だけを晒す前提のLaravelが流入した結果、危険な配置が事実上の標準になってしまいました。各社の公式ドキュメントすら public_html 内にプロジェクトを置く例ばかりです。だから対策は個人の注意力ではなく仕組みにするのが正解です。具体的には、デプロイ手順のデフォルトを変えます。

正しい配置を図で押さえる

✗ 危険(本体を公開ルート直下)

public_html/
├─ .env   ← 読める!
├─ .git/  vendor/
└─ public/

✓ 安全(本体は外、public だけ)

app-laravel/   ← 公開ルート外
├─ .env  .git/  ← 到達不能
└─ public/ → public_html から require
本体はdocrootの外、公開するのは public/ だけ。これだけで .env は到達不能になる。

自分の環境で、どの対策ができるか

同じ「.env を外に出さない」でも、サーバーの種類や権限によって取れる手段が違います。まず自分がどれに当たるかを確認してから、下の Step に進みます。

環境恒久対策(本命)応急処置注意点
共用レンタルサーバー(SSHあり)本体を公開フォルダの外へ移し、公開フォルダには小さな index.php だけ置く(Step 2).htaccess で拒否公開フォルダ自体を symlink にすると 500 になる環境がある
共用レンタルサーバー(FTPのみ)FTPソフトで、本体を公開フォルダ(public_html など)の1つ上の階層へアップロードし直す.htaccess で拒否多くの共用サーバーでは公開フォルダの上にも書き込めるが、できない場合はサポートに確認する
管理画面で公開フォルダを指定できる公開フォルダを app-laravel/public に直接向ける不要(ただし確認は必要)指定を変えたら、古い公開フォルダに本体のコピーが残っていないか消す
VPS(Apache)VirtualHost の DocumentRoot を public/ に向ける設定ファイルに拒否ルールAllowOverride None だと .htaccess は読まれない(Apache 2.4 の既定値)
VPS(Nginx)root を public/ に向け、ドットで始まるファイルを拒否する設定ファイルに拒否ルールNginx は .htaccess を読まない

どの環境でも、ゴールは同じです。公開フォルダの中に .env・.git・vendor/・storage/ が存在しない状態にします。Laravel の公式ドキュメントも、index.php をプロジェクトのルートへ移して公開すると多くの機密設定ファイルがインターネットに見えてしまう、と注意しています。

Step 1:応急処置(すぐできて元に戻せる)

公開ルートの .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>
 
# /<app>/storage 等の内部ディレクトリが見えている場合
RedirectMatch 403 (?i)^/[^/]+/(storage|bootstrap/cache|config|database|resources|routes|tests|vendor)(/|$)
# === END ===

応急処置はゴールではない

ブラックリストでは composer.lock や .gitignore など、拒否し忘れるファイルが必ず出ます。あくまで Step 2 までの時間稼ぎと考えます。

Step 2:構造改修(恒久対策)

アプリ本体を公開ルートの外へ移し、公開ルートには「読み込むだけの小さな入口」を置きます。

# 1. 本体を公開ルートの外へ(同一ファイルシステムなら mv は一瞬・atomic)
mv ~/example/public_html/app  ~/example/app-laravel
 
# 2. 公開ルートに最小の入口だけ作る
mkdir -p ~/example/public_html/sub
cat > ~/example/public_html/sub/index.php <<'PHP'
<?php require __DIR__ . '/../../app-laravel/public/index.php';
PHP
 
# 3. Laravel public/.htaccess をコピー、静的アセットを symlink
# 4. 設定キャッシュをクリア
rm -f ~/example/app-laravel/bootstrap/cache/{config,services,packages,routes-v7}.php

当サイトが運用で学んだ注意点:symlinkより bootstrap-redirect

「public_html 自体を public/ へのsymlinkにする」やり方は、登録済みサブドメインのdocrootだと追従せず500になる環境があります。docrootは実ディレクトリのまま、中の小さな index.php から絶対パスで require する「bootstrap-redirect型」のほうが、スコープ制約のある共用環境で安定します。さらに opcache が壊れたロードを記憶して500を返し続けることがあり、最終手段は入口ファイル名を変える(entry.php 等)です。

Step 3:鍵ローテーション(漏れていた前提で)

公開されていた期間があるなら、.env の鍵は見られた前提で入れ替えます。優先順は「外部API・OAuthシークレット → 暗号鍵 → メール → DB」。.env の項も参照。OAuthのシークレット再発行は refresh_token を無効化することがあるので、再認可フローを用意してから実施します。

Step 4:自己点検(習慣化)

最後に、自分のサイトで実際に漏れていないか確認します(自分の所有ドメインに対してのみ)。

# 200 で本文が返ったら公開されている。403/404 なら一旦OK
curl -sI https://あなたのドメイン/.env | head -1
curl -sI https://あなたのドメイン/.git/config | head -1

これを公開のたびに確認する癖をつけると、配置ミスを早期に発見できます。

自分の環境で確認する方法(詳しく)

Step 4 の2行に加えて、次の点も確認しておくと見落としが減ります。どれも自分のサーバー・自分のドメインに対してだけ行ってください。

1

ステータスコードだけでなく中身を見る

アプリによっては、存在しないURLにもトップページを 200 で返すことがあります。その場合、ステータスだけでは判断できません。curl -s https://あなたのドメイン/.env | head -c 120 のように先頭だけ表示し、APP_KEY= や DB_PASSWORD= のような設定の行が出ないかを見ます。.git/HEAD なら ref: refs/heads/ で始まる行が出たら公開されています。

2

アプリの数だけ確認する

トップのドメインだけでなく、サブドメインやサブディレクトリに置いたアプリごとに同じ確認をします。配置ミスはアプリを増やすときにコピーされやすく、1つ直しても別の場所に残っていることがあります。

3

サーバー側で公開フォルダの中を探す

SSHが使えるなら、公開フォルダの中に危ないファイルが残っていないかを直接探します。find ~/public_html -maxdepth 4 \( -name ".env*" -o -name ".git" -o -name "*.sql" -o -name "*.zip" \) のように実行し、何も出なければ正常です。FTPしか使えない場合は、FTPソフトで隠しファイルを表示する設定にしてから一覧を見ます。

4

.htaccess が本当に効いているか確かめる

秘密を含まないダミーのファイル(例:中身が test だけの check.bak)を公開フォルダに置き、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年8月、あるセキュリティ企業の調査部門は、Webサーバー上で公開されていた .env を自動で集め、中に書かれたクラウドの認証情報を使ってデータを持ち出し、身代金を要求する活動を報告しました。報告によると、対象になったドメインは約11万、集められた環境変数は9万件を超え、そのうち約7,000件がクラウドサービス、約1,500件がSNSアカウントの認証情報でした。

報告は、原因をクラウド側の脆弱性ではなく、被害組織が誤って .env を公開していた設定ミスだとしています。ここから分かることは2つです。

  • 公開された .env は、人が偶然見つけるのではなく、プログラムで大量に探されている
  • 1つの .env から、クラウドやメールなど別のサービスへ被害が広がる

小さなサイトだから見つからない、とは考えないでください。同じ配置ミスを匿名化した事例は Laravelの .env が外部から見られる状態だった事例 にまとめています。

次に読む

出典

  • Laravel 公式ドキュメント「Deployment」(public/index.php に向ける・プロジェクトのルートから公開しない・本番では APP_DEBUG=false): laravel.com
  • Laravel 公式ドキュメント「Configuration」(.env をソース管理に入れない、Debug Mode): laravel.com
  • Laravel 公式ドキュメント「Encryption」(APP_KEY の差し替えと APP_PREVIOUS_KEYS): laravel.com
  • Apache HTTP Server 2.4「core - AllowOverride」(None のとき .htaccess は読まれない、既定値は 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」(2024年8月15日): unit42.paloaltonetworks.com

よくある質問

Qとりあえず今すぐできる対策は?
A

公開ルートの .htaccess に『.env・.git・SQLダンプ・credentials.json などを拒否する』ブロックを追記する応急処置です。可逆で通常表示に影響しません。ただしこれは一時的な対策で、根本的な対策は構造改修です。

Qなぜ .htaccess だけだと不十分なの?
A

ブラックリストは、新しい名前のファイルが増えるたびに拒否ルールを足す必要があり、必ず漏れが出るからです。アプリ本体を公開ルートの外に出せば、.env がそもそも到達不能になり、ルールに頼らず安全になります。

Qsymlink で public/ を晒すのではダメ?
A

環境によっては、登録済みサブドメインのdocrootを後からsymlinkで別ツリーに向けると追従せず500になります。当サイトの経験では、docrootは実ディレクトリのまま、中の小さな index.php から絶対パスで require する『bootstrap-redirect型』のほうが安定します。

Q自分のサイトの .env が外から見えていないか確認する方法は?
A

自分が所有するドメインに対して、curl やブラウザで /.env と /.git/HEAD を開き、HTTPステータスと中身を見ます。403や404なら一旦は大丈夫です。200で『APP_KEY=』のような設定の行が見えたら公開されています。サブドメインやサブディレクトリにアプリが複数あるなら、その数だけ確認します。SSHが使えるなら、公開フォルダの中に .env や .git が無いかをサーバー側でも探します。

QNginx のサーバーや .htaccess が効かない環境ではどうする?
A

Nginx は .htaccess を読みません。Apache でも、AllowOverride が None の設定なら .htaccess は無視されます。この場合は、サーバーの設定ファイルで公開ルート(root や DocumentRoot)を Laravel の public/ に向けるのが基本です。Laravel の公式ドキュメントにある Nginx の設定例にも、ドットで始まるファイル(.well-known 以外)を拒否する行が入っています。

Q.env が公開されていたと分かったら、ファイルを消すだけで十分?
A

十分ではありません。公開されていた間に誰かが取得していれば、ファイルを消しても中の鍵はそのまま使えます。APIキー・OAuthシークレット・DBやメールのパスワード・APP_KEY を再発行し、各サービスの利用履歴に心当たりのない操作がないか確認します。.git も公開されていたなら、過去のコミットに含まれていた秘密も漏れた前提で扱います。