対策・学習9分で読める

Laravelの.envが外部から見られる状態だった事例:共用レンタルサーバーでの配置ミスと直し方

共用レンタルサーバーのLaravelアプリで、.env・.git・DBダンプがHTTPで誰でも読める状態になる配置ミスの事例です。原因はプロジェクトをpublic_html直下に置いたこと。応急処置・キーの再発行・構造改修の三段階で直します。

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

共用レンタルサーバーで実際によく起きる設定ミスを、ドメイン名や具体値をすべて伏せた事例として教訓にした記事です。他人のサイトを探す話ではありません。狙いは「自分の(あるいは引き継いだ)デプロイを安全にする」ことです。

事故サマリー — INCIDENT FILE
種別
.env / .git / DBダンプ の公開(設定ミス)
重大度
Critical(全シークレットがHTTPで読める状態)
原因
Laravel本体を public_html 直下に配置(本来 public/ だけ公開)
伝播
アプリを増やすたび同じ配置ミスがコピー=数十個に拡大
恒久対処
本体を docroot の外へ+ public/ だけ symlink

一番危ないのは .env と .git

.env は DB 認証・メール・外部API・暗号鍵(APP_KEY)まで、すべての秘密がまとめて入ったファイル。.git が公開されていると、現在値だけでなく git 履歴を辿って過去のローテーション分まで復元されます。詳しくは用語辞典の .env とは を参照。

何が起きていたか

共用レンタルサーバー上で、多数のLaravelアプリがこういう配置になっていた。危険な配置と正しい配置を並べます:

危険な配置(やりがち)

~/public_html/
└── app/          ← Laravel丸ごと(間違い)
    ├── .env      ← /app/.env で読める
    ├── .git/     ← clone で履歴ごと復元
    ├── vendor/  config/  storage/
    └── public/   ← 本当はここ「だけ」

正しい配置

~/laravel/app/    ← 本体は公開ルート外
├── .env  .git/   ← HTTPから届かない=安全
└── public/       ← ここだけ symlink で公開
        └── index.php

外部から 200 OK で取得できてしまっていた典型ファイル:

ファイル何が危険か
.env / .env.bk_*DB認証・メール・外部APIキー・APP_KEY が平文で全部
.git/ 一式git clone でソースと全履歴を復元できる
composer.lock / package-lock.json依存ライブラリの正確なバージョン=既知CVEを狙う手がかり
credentials.jsonOAuthクライアント秘密鍵がまとめて
*.sql / db-*.sql.gzDBの全件ダンプが圧縮で数十MB

特に危険なのは、同じミスが広がること

一度この配置をすると、同種アプリを足すたびに同じ配置ミスがコピーされます。1個の設定ミスが数十個に増える。「気づいた時点で全部点検する」が基本です。

直し方:三段階

Step 1 — 応急処置:.htaccess で危険ファイルをHTTPレベルで遮断

まず「これ以上漏れる」を止めます。可逆で、通常表示には影響しません。public_html/.htaccess の冒頭に、機密ファイルへのアクセスを拒否するブロックを足します(防御目的の設定例):

# === SECURITY BLOCK ===
# .env / .git 配下を 404 に
RedirectMatch 404 (?i)/\.(env|git)(\..*)?(/|$)
 
# バックアップ・ダンプ・認証ファイルを拒否
<FilesMatch "(?i)\.(sql|sql\.gz|bak|old|swp|save|orig|tgz)$|^credentials\.json$|\.bk[._]">
    Require all denied
</FilesMatch>
 
# Laravel の内部ディレクトリが /<app>/storage/... 等で見えていた場合
RedirectMatch 403 (?i)^/[^/]+/(storage|bootstrap/cache|config|database|resources|routes|tests|vendor)(/|$)
# === END ===

ただしブラックリストは、対象のファイルが増えるたびに追加が必要で、必ず漏れが出ます。composer.lock を塞ぎ忘れる、新しい命名のファイルが対象から漏れる、といったことが起きます。応急処置と割り切り、根本対策は Step 3 で行います。

Step 2 — 鍵ローテーション(漏れた前提で)

.htaccessで塞いだ後でも、すでに見られている前提で扱います。高リスク順に:

  1. 外部APIキー(最優先・即時):課金や乗っ取りに直結。OAuthのCLIENT_SECRET、各種APIキー、クラウド認証情報は無効化+再発行+最小権限で。
  2. OAuthクライアントシークレット。
  3. APP_KEY:再生成するとセッションが切れ、暗号化済みDBカラムが復号不能になることがあるので影響範囲を先に確認。
  4. メールSMTP、DB認証情報。

ローテーションで実際に起きた失敗

OAuthによっては、クライアントシークレットを再発行すると既存の refresh_token も同時に無効化されます。保存済みトークンでの再取得が失敗して本番障害に。必ず「再認可フロー」を用意してからシークレットを再発行してください。さらに、ローカル開発側の .env / 設定も同時に更新しないと、次のデプロイで旧値が復活します。

Step 3 — 構造改修:アプリ本体をdocrootの外へ(根本対策)

各アプリをこう再配置します:

# 1. 本体を公開ルートの外へ(同一ファイルシステムなら mv は一瞬で atomic)
mv ~/example/public_html/app  ~/example/app-laravel
 
# 2. 公開ルートには最小の bootstrap だけ置く
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、設定キャッシュをクリア

この形にすれば、.env も .git も vendor/ もそもそも公開ルートに存在しないので、.htaccessルールに頼らずに安全になります。具体的な手順は レンタルサーバーで .env を公開しないための配置と設定 にまとめました。

✗ 危険:本体が公開ルートの内側

— 公開境界(HTTPで読める)—

.env ・ .git ・ vendor ・ public

↑ すべてが線の内側=誰でも読める

✓ 安全:本体は公開ルートの外側

— 公開境界(HTTPで読める)—

public/ だけ(symlink)

.env ・ .git は線の外=届かない

分かれ目は1つ、公開ルートの内か外か。.env と .git が公開ルートの内側にあれば、誰でもHTTPで読める。

修正中につまずいた点

  • サブドメインのスコープ問題:登録済みサブドメインのdocrootを後からsymlinkで別ツリーに向けても追従せず500になることがある。→ docrootは実ディレクトリのままにして、中の小さなindex.phpから絶対パスrequireで読む「bootstrap-redirect型」が安定。
  • opcacheが壊れた状態を覚え続ける:index.phpのロード失敗をopcacheが記憶し、作り直しても500を返し続けることがある。最終手段はファイル名を変える(entry.php等)。
  • 「サブドメイン未登録」のdocrootが親ドメイン経由で読める:https://sub.example/ は無効でも https://example/sub/ で中身が見える、という見落としやすい経路。点検時は親ドメイン経由パスも塞ぐ。
  • config/*.php への秘密値ハードコード:.git公開と組み合わさると履歴から全ローテーションが復元される。常にenv()経由にし、fallback既定値に実値を書かない。

教訓:この配置ミスはサーバーの初期構成から起きやすい

レンタルサーバー初期の「public_html直下に全部置く」慣習のところに、public/だけを公開する前提のLaravelが広まった結果、「アプリ本体をpublic_html/<app>/に置き、その下のpublic/を見せる」という危険な配置が事実上の標準になってしまった。各社の公式ドキュメントが「public_htmlの中にプロジェクトを置く」例ばかりなのも、この配置が広まった一因だ。

だから対策は個人の注意力ではなく仕組みにする:

再発を仕組みで防ぐ

  • デプロイ手順書のデフォルトを「本体はdocrootの外、public/だけsymlink」にする
  • CI/lintで「.envをgitに含めない」「public_html/<app>直下にプロジェクトを置かない」を機械チェック
  • 公開後に自分のサイトの .env/.git が外から取れないかを定期確認(自己点検)

当サイト はこの「自分のサイトを自分で点検する」習慣化を学習の軸にしています。→ 超入門:.env と APIキーは何が危ないのか

次に読む

よくある質問

Q.env が公開されていたら、何を最優先で対応すべき?
A

まず .htaccess 等で漏洩経路を即遮断。次に .env にあった外部APIキー・OAuthシークレットを高リスク順にローテーション(すでに見られた前提)。最後にアプリ本体を公開ディレクトリの外へ移す構造改修で、ルールに頼らず安全な状態にします。

Qなぜ public_html 直下にLaravelを置くと危ないの?
A

Laravelは public/ だけを公開する前提の構造です。プロジェクト全体をdocrootに置くと、その上にある .env(全シークレット)・.git(履歴ごと復元可能)・vendor/ などまで丸ごとHTTPで読めてしまいます。

Q.htaccess のブラックリストで塞げば十分?
A

応急処置としては有効ですが、新しい名前のファイルが増えるたびにルールの追加が必要になり、漏れが出ます。根本的な対策は『アプリ本体をdocrootの外に置き、public/ だけをsymlinkで公開する』構造改修です。