事故図鑑
Laravelアプリの .env が全世界に公開されていた——共用レンタルサーバーで一番ありがちな配置ミス
共用レンタルサーバーに置いたLaravelアプリで、.env・.git・DBダンプ・OAuth秘密鍵がHTTPで誰でも読める状態になる——現場で実際によくある配置ミスの事例。原因は『プロジェクトをpublic_html直下に置いた』配置。応急処置→鍵ローテ→構造改修の三段階と、なぜこれが個人のミスでなく業界的な“悪手の標準化”なのかを、防御目線でまとめます。
共用レンタルサーバーで実際によく起きる設定ミスを、ドメイン名や具体値をすべて伏せた事例として教訓にした記事です。他人のサイトを探す話ではありません。狙いは「自分の(あるいは引き継いだ)デプロイを安全にする」ことです。
- 種別
- .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.json | OAuthクライアント秘密鍵がまとめて |
*.sql / db-*.sql.gz | DBの全件ダンプが圧縮で数十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で塞いだ後でも、すでに見られている前提で扱います。高リスク順に:
- 外部APIキー(最優先・即時):課金や乗っ取りに直結。OAuthの
CLIENT_SECRET、各種APIキー、クラウド認証情報は無効化+再発行+最小権限で。 - OAuthクライアントシークレット。
APP_KEY:再生成するとセッションが切れ、暗号化済みDBカラムが復号不能になることがあるので影響範囲を先に確認。- メール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 を公開しないための配置と設定 にまとめました。
道中の“教科書に載っていない”罠
- サブドメインのスコープ問題:登録済みサブドメインの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が外から取れないかを定期確認(自己点検)
ITD はこの「自分のサイトを自分で点検する」習慣化を学習の軸にしています。→ 超入門:.env と APIキーは何が危ないのか
次に読む
- 用語:.env(環境変数ファイル)とは / CVE とは
- 対策:レンタルサーバーで .env を公開しないための配置と設定
- 事故:AIで書いたコードからAPIキーが漏れた話(漏洩の“もう一つの経路”=ランタイム)
よくある質問
Q.env が公開されていたら、何を最優先で対応すべき?
まず .htaccess 等で漏洩経路を即遮断(出血を止める)。次に .env にあった外部APIキー・OAuthシークレットを高リスク順にローテーション(すでに見られた前提)。最後にアプリ本体を公開ディレクトリの外へ移す構造改修で、ルールに頼らず安全な状態にします。
Qなぜ public_html 直下にLaravelを置くと危ないの?
Laravelは public/ だけを公開する前提の構造です。プロジェクト全体をdocrootに置くと、その上にある .env(全シークレット)・.git(履歴ごと復元可能)・vendor/ などまで丸ごとHTTPで読めてしまいます。
Q.htaccess のブラックリストで塞げば十分?
応急処置としては有効ですが本質的にモグラ叩きです。新しい名前のファイルを足すたびに穴が増えます。本命は『アプリ本体をdocrootの外に置き、public/ だけをsymlinkで晒す』構造改修です。