はじめに
本番環境でphp artisan migrate:fresh
実行して真っ青になった経験、ありませんか?
私はあります。(小声)
今回はLaravelで提供されているprohibitDestructiveCommands
メソッドを使って、本番環境での破壊的コマンドの実行を防ぐ方法を紹介します。
何が怖いのか
Laravelには便利だけど本番環境で絶対に実行してはいけないコマンドがいくつかあります。
# データベースの全テーブルを削除
php artisan db:wipe
# マイグレーションをリセットして再実行
php artisan migrate:fresh
# マイグレーションをロールバックして再実行
php artisan migrate:refresh
# マイグレーションを全てロールバック
php artisan migrate:reset
# 最後のマイグレーションをロールバック(Laravel 11.39以降)
php artisan migrate:rollback
これらのコマンドは開発環境では超便利なんですが、本番環境で実行してしまうと...
まぁ、想像したくないですね。
解決策
実はLaravelには、こういった事故を防ぐための仕組みが用意されています。
AppServiceProvider
に以下のコードを追加するだけです。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\DB;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
DB::prohibitDestructiveCommands(
$this->app->isProduction()
);
}
}
たったこれだけ。
仕組み
DB::prohibitDestructiveCommands()
メソッドは、引数にtrue
が渡されると、以下のコマンドの実行を禁止します。
db:wipe
migrate:fresh
migrate:refresh
migrate:reset
-
migrate:rollback
(Laravel 11.39以降)
$this->app->isProduction()
は、APP_ENV
がproduction
の時にtrue
を返すので、本番環境でのみこれらのコマンドが禁止される仕組みです。
実際に試してみる
本番環境での動作
# .envで APP_ENV=production に設定した状態で
php artisan migrate:fresh
実行すると以下のようなエラーが出ます。
Destructive commands are prohibited in production.
うん、安心。
開発環境での動作
# .envで APP_ENV=local の状態で
php artisan migrate:fresh
通常通り実行されます。開発環境では今まで通り使えるので、開発効率が落ちる心配もありません。
さらなる安全対策
基本的には上記の設定だけで十分ですが、さらに安全性を高めたい場合は以下の対策も検討してください。
1. 環境変数での追加制御
特定の環境変数がある場合のみ破壊的コマンドを許可する、みたいな実装も可能です。
public function boot()
{
$shouldProhibit = $this->app->isProduction()
&& !env('ALLOW_DESTRUCTIVE_COMMANDS', false);
DB::prohibitDestructiveCommands($shouldProhibit);
}
2. カスタムミドルウェアの実装
特定のIPアドレスからのみ実行を許可するとか、そういった制御も可能です。
3. データベースユーザーの権限設定
本番環境のデータベースユーザーには、そもそもDROP
権限を与えない、というのも有効な対策です。
-- 必要最小限の権限のみ付与
GRANT SELECT, INSERT, UPDATE, DELETE ON database.* TO 'app_user'@'localhost';
まとめ
DB::prohibitDestructiveCommands()
を使うと、本番環境での破壊的コマンドの実行を簡単に防げます。
実装も簡単だし、開発環境には影響しないし、導入しない理由がないですね。
まだ設定していない方は、この記事を読み終わったらすぐにAppServiceProvider
に追加しておきましょう。
未来の自分(とチームメンバー)を守るために。