対象:Laravelを本番で運用している人。ここでは攻撃手順は扱わず、本番を固めるための実務リファレンス(優先度つきチェックリスト・危険な既定・領域別対策・自己検証)を示します。フレームワーク全体の考え方は フレームワーク別セキュリティの入口 も参照。
優先度つきハードニング・チェックリスト
まずこの表を上から実施します。P0は他対策の前提、P1が最頻の事故源、P2は継続的な運用衛生です。
P0 ── 前提(まずこれ)
本番デバッグ無効 / 秘密を公開面の外+権限600 / APP_KEY 安全管理
P1 ── 最頻の事故源
認可(Policy/Gate) / Mass Assignment制御 / 依存CVE監視 / 本番キャッシュ
P2 ── 運用衛生
セッション/CSRF/Cookie / アップロード検証 / HTTPS・ヘッダ・レート制限
| 優先 | 対策 | 具体(Laravel) |
|---|---|---|
| P0 | 本番デバッグ無効 | APP_DEBUG=false / APP_ENV=production を config:cache で固定。例外画面に内部を出さない |
| P0 | 秘密を公開面の外へ | .env・バックアップ・鍵は public/ 外。権限600。storage/logs も非公開 |
| P0 | APP_KEY を安全管理 | 暗号化・署名Cookie・セッションの土台。環境から注入・漏洩時ローテーション |
| P1 | 認可を明示 | Policy / Gate + authorize()・can ミドルウェアで所有者/権限スコープ |
| P1 | Mass Assignment 制御 | $fillable 明示。$request->all() の丸ごと代入を避け validated() を使う |
| P1 | 依存(Composer)CVE監視 | composer audit / osv-scanner で監視・実稼働版で判定し即パッチ |
| P1 | 本番キャッシュ | config:cache route:cache view:cache で確実な反映+高速化 |
| P2 | セッション/Cookie 安全属性 | secure http_only same_site を設定。ログイン時にセッション再生成 |
| P2 | アップロード検証 | 種別・サイズ・保存先を検証。保存は public/ 外、実行権限なし |
| P2 | HTTPS/ヘッダ/レート制限 | HTTPS強制・HSTS等・ログイン/APIに throttle |
1. 秘密と APP_KEY(P0)
Laravelは既定で .env をドキュメントルート外(プロジェクト直下)に置きます。事故は主に**「本来置かない場所に秘密を置く」**ことで起きます。
.env・DBダンプ・バックアップ・鍵ファイルをpublic/に置かない。秘密はアプリルート外・権限600(所有者のみ)。storage/やstorage/logs/を公開しない(ログに秘密や個人情報が出ることがある)。.envはリポジトリにコミットしない。APP_KEYは暗号化・署名付きURL・暗号化Cookie・セッションの土台。環境変数から注入し、漏洩したら速やかにローテーション(既存の暗号化データ/セッションは無効化される点に注意)。
秘密の置き場所の一般論は 公開ディレクトリに秘密を置かない、実際の全公開事例は .env全公開の事例 を参照。
2. 本番設定:DEBUG・環境・キャッシュ(P0)
APP_DEBUG=false / APP_ENV=production
設定をキャッシュして固定
php artisan config:cache(+route:cache view:cache)で確実に反映+高速化。注意:config:cache 後は config 配下の外で env() が null になるため、アプリ本体では config('...') 経由で参照する。診断ツールを本番に露出しない
3. 認可:Broken Access Control(P1・最多の事故源)
実運用で最も多いのが「認証はあるが認可が無い」型です。ログインできること=その操作をしてよいこと、ではありません。
やりがち(危険)
- 認可を書かず「ログイン=閲覧/更新可」
- ルートモデル束縛で他人のIDでも取得できる
Model::create($request->all())で全フィールド受け入れis_admin等の権限フィールドをユーザー入力から代入
正しい
- Policy / Gate+
authorize()・canミドルウェアで所有者/権限を毎回スコープ - 取得も
where('user_id', $me)等で所有者に限定 $request->validated()(FormRequest)で受け入れを限定$fillableを明示し Mass Assignment を遮断
用語の詳細は IDORとは。取得・更新・削除のすべての経路で所有者チェックを通すのが要点です。
4. インジェクションと出力
Laravelの Eloquent/クエリビルダはプレースホルダで値をバインドし、Blade は既定で出力をエスケープします。この安全機構を自分で外さないのが基本です。
- SQL:
whereRaw・DB::rawに文字列連結でユーザー入力を混ぜない。生SQLが要る場面でもバインドパラメータを使う(→ SQLインジェクションとは)。 - XSS:Blade の
{{ }}はエスケープ、{!! !!}は生出力。ユーザー入力を{!! !!}に渡さない。どうしてもHTMLを出すならサニタイズ後に限定(→ XSSとは)。 - 検証:入力は FormRequest /
validate()で型・範囲・許可値を検証してから使う。
5. セッション・CSRF・Cookie(P2)
- CSRF:web ミドルウェアの CSRF 保護を全体無効化しない。フォームに
@csrf、SPAは適切なトークン運用(→ CSRFとは)。 - Cookie/セッション:
config/session.phpでsecure(HTTPS)・http_only・same_siteを設定。ログイン時にセッションを再生成してセッション固定を防ぐ(Laravelの認証は再生成する)。 - ブルートフォース:ログインに
throttle/認証試行制限をかける。
6. ファイルアップロードと公開ファイル(P2)
- アップロードは種別(mime)・拡張子・サイズを検証し、クライアント提供のファイル名を信用しない。
- 保存先は
public/外(storage/等)にし、実行権限を与えない。公開が必要なものだけ配信経路を用意する。 - 連番の直リンクで他人のファイルを取得されないよう、配信にも認可を通す。
7. HTTPS・セキュリティヘッダ・レート制限(P2)
- HTTPS強制(
URL::forceScheme('https')等)+ HSTS。ロードバランサ配下ではTrustProxiesで正しいスキームを認識させる。 - セキュリティヘッダ(X-Content-Type-Options 等・CSPはアセット構成に合わせて設計)。自サイトの状態は セキュリティヘッダ診断 で確認できる。
- レート制限:ログイン・API・パスワードリセットに
throttleを適用。
8. 依存とバージョン(P1)
- Composer依存のCVEを
composer auditや osv-scanner で監視し、実稼働版で判定して即パッチ(→ 依存のCVE監視 / CVE対応の実務)。 - Laravel本体とPHPをサポート対象バージョンに保つ。EOL版を放置しない(直らない穴が積み上がる)。
検証:自分のLaravelは固められているか
作ったら終わりでなく、点検して初めて完了です。以下は自分のサイト/環境に対する防御的な自己チェックです。
秘密がURLで取れないか
/.env や /storage/logs/laravel.log にアクセスして404になることを確認(取得できたら即対処+鍵ローテーション)。本番でデバッグが露出していないか
認可が効いているか
Cookie/セッションと依存
composer audit がクリーンか確認。当サイトの視点:堅い既定でも『認可と依存』は自分の責任
Laravelは良い既定を多く持ちますが、「誰が・何を・してよいか」という認可と依存の鮮度だけは、アプリ固有・運用固有なのでフレームワークが自動で守れません。当サイトが繰り返し見る事故も、凝った攻撃より「認証はあるのに所有者チェックが無い」「本番でデバッグが開いていた」「秘密が露出していた」という設定・運用の型が中心です。だから守りの重心は、上の表を上から潰し、取得・更新のたびに所有者でスコープし、依存をCVE監視するという地味な徹底にあります。
次に読む
- 入口:フレームワーク別セキュリティ(入口) / Next.jsのセキュリティ対策
- 秘密:公開ディレクトリに秘密を置かない / 事例:.env全公開の事例
- 認可・実務:IDORとは / 脆弱性(CVE)対応の実務 / 依存のCVE監視
- 用語:SQLインジェクション / XSS / CSRF
よくある質問
QLaravelで最初にやるべきセキュリティ対策は何ですか?
優先度P0の3つです。(1)本番で APP_DEBUG=false を確実にし、設定をキャッシュ(config:cache)して固定する、(2).env と秘密ファイルを公開ディレクトリ(public/)の外に置き権限を絞る、(3)APP_KEY を安全に管理する(暗号化・署名Cookie・セッションの土台なので漏洩はローテーション)。この3つは他の対策の前提になります。次に認可(Policy/Gate)と依存(Composer)のCVE監視へ進みます。
QAPP_DEBUG=true のまま本番に出すと何が危険ですか?
デバッグが有効だと、例外画面にスタックトレースだけでなく設定値・環境変数・接続情報などの内部が表示され得ます。攻撃者はわざと例外を起こして情報を引き出せます。本番では APP_DEBUG=false・APP_ENV=production を設定し、config:cache で確実に反映してください。あわせて Telescope / Debugbar など診断ツールを本番で公開しないこと。
Qconfig:cache を使うと env() が null になるのはなぜですか?
config:cache は設定を1ファイルにコンパイルして高速化しますが、その後は config ファイルの外で env() を呼ぶと null が返ります(キャッシュ時点の .env しか読まれないため)。対策は、env() は config/ 配下でのみ使い、アプリ本体では config('...') 経由で参照することです。本番では config/route/view をキャッシュするのが基本で、設定の確実な反映と高速化を両立できます。
Q『ログインしていれば見られる』を防ぐには?
認証(ログイン)と認可(その操作の可否)は別物です。Laravelでは Policy / Gate を実装し、コントローラで authorize() や can ミドルウェアを使って、取得・更新・削除のたびに所有者や権限をスコープします。あわせて Mass Assignment を $fillable の明示で制御し、$request->all() の丸ごと代入を避けます。これがないと、URLのIDを差し替えるだけで他人のデータに到達します(IDOR)。
Q依存パッケージ(Composer)の脆弱性はどう管理しますか?
composer audit や osv-scanner で既知CVEを機械監視し、『実稼働版』で判定して素早くパッチします(composer.json の宣言でなく、実際に入っている版で判断)。加えて Laravel 本体と PHP をサポート対象のバージョンに保ち、EOL版を放置しないこと。依存の鮮度は、凝った攻撃より事故を分ける現実的な要因です。