🔑

Laravel認証のセキュリティを強化してみた

に公開

目次


はじめに

Laravelで認証機能を実装するなら、BreezeやJetstreamを使えばすぐにログイン画面まで作れます。
でも、そのまま本番運用して大丈夫? と不安になったことはありませんか?

顧客情報を扱うWebアプリケーションでは、利便性だけでなくセキュリティの高さが求められます。
Laravelは便利な認証機能を提供してくれますが、初期設定のままだと本番運用に必要な強度を満たしていないことがあります。

特に、顧客データや個人情報を取り扱うサービスでは、パスワードの強度・ログイン試行制限・暗号化方式など、
セキュリティの土台をしっかり固めることが不可欠です。

この記事では、 デフォルトの認証機能を本番環境でも安心できるレベルまで引き上げるための実装手順を解説します。

この記事で学べること

  • Laravel Breezeの認証の弱点とリスク
  • 強力なパスワードポリシーの設定方法
  • ログイン試行回数制限(レートリミット)の導入
  • 最新暗号化アルゴリズム(Argon2id)の設定
  • 本番・開発環境の設定分離方法
  • テスト環境でのセキュリティ検証

対象読者

  • Laravelで認証機能を使ったことがある方
  • セキュリティ対策を強化したい方
  • 本番運用を意識して開発している方

開発環境

PHP: 8.3+
Laravel: 12.x
Laravel Breeze: 2.x
テスト: PHPUnit(SQLite)

1. Laravel Breezeの課題とリスク

Breezeは軽量でシンプルな認証機能を提供してくれますが、そのままだと以下のリスクがあります。

  • パスワードが8文字以上だけ → 簡単なパスワードも通ってしまう
  • ログイン試行制限がない → ブルートフォース攻撃に弱い
  • 漏洩パスワードの使用制限なし → 流出済みのパスワードでも使える
  • 暗号化方式が古い場合がある → bcrypt固定のケースが多い

2. セキュリティ強化の実装手順

ステップ1: 強力なパスワードポリシー

app/Providers/AppServiceProvider.php に設定を追加します。

use Illuminate\Validation\Rules\Password;

Password::defaults(function () {
    $base = Password::min(12)   // 12文字以上
        ->letters()             // 英字
        ->mixedCase()            // 大文字・小文字
        ->numbers()              // 数字
        ->symbols();             // 記号

    return app()->isProduction()
        ? $base->uncompromised(3) // 漏洩パスワードチェック(3回以上検出で拒否)
        : $base;                  // 開発ではオフ(通信負荷軽減)
});

💡 ポイント
文字数・種類の組み合わせを増やすと総当たり攻撃が難しくなる
開発環境では漏洩チェックをオフにして開発スピード確保

ステップ2: ログイン試行回数制限(レートリミット)

同じく AppServiceProvider.php に追記します。

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Http\Request;

RateLimiter::for('login', function (Request $request) {
    $byEmailAndIp = strtolower((string)$request->input('email')).'|'.$request->ip();

    return [
        Limit::perMinute(5)->by($byEmailAndIp)->response(function () {
            return back()->withErrors([
                'email' => __('auth.throttle')
            ])->setStatusCode(429);
        }),
        Limit::perMinute(20)->by($request->ip()),
    ];
});

💡 ポイント
同一メール+IPで5回/分まで
IP単位でも20回/分までに制限
過剰な試行を防ぐ

ステップ3: 新規登録の試行制限

RateLimiter::for('register', function (Request $request) {
    return [
        Limit::perMinute(3)->by($request->ip())->response(function () {
            return back()->withErrors([
                'email' => __('auth.throttle')
            ])->setStatusCode(429);
        }),
    ];
});

💡 ポイント
bcryptよりも耐GPU攻撃性が高い
メモリ使用量を増やすと攻撃コストが上がる

テスト環境でのセキュリティ検証

phpunit.xml にテスト用DB設定を追加。

<php>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
</php>

そして、パスワードポリシーやレート制限が動作するかをテストコードで確認します。

5. 環境ごとの最適設定

本番の設定

HASH_DRIVER=argon2id
SESSION_DRIVER=redis

開発

HASH_DRIVER=bcrypt
SESSION_DRIVER=file

6. セキュリティ強化の効果比較

項目 強化前 強化後
パスワード強度 8文字以上 12文字+記号+数字
ログイン制限 なし 5回/分
暗号化方式 bcrypt Argon2id

7. トラブルシューティング

・ログイン制限で開発中にブロックされる → .envで制限を緩める
・漏洩チェックでAPI遅延 → 開発環境ではオフにする

8. まとめ

Laravelの認証機能は便利ですが、デフォルトのままだと本番環境では心もとないため、
この記事の通り、セキュリティを強化してみました。

セキュリティ対策は「やりすぎ」くらいでちょうどいいと言われます。
もし、これからLaravelで本番運用を考えている方の参考になれば嬉しいです 🙌

Discussion