
laravel-threat-detection v1.7.2
SQLインジェクション、XSS、RCE、ボットスキャナー、175以上の攻撃パターンを検出・記録するパッシブなLaravelミドルウェア。内蔵ダッシュボード、Slackアラート、REST API、地理情報エンリッチメントを搭載。WAFではなくIDS。
Laravel Threat Detection
Laravel 向けのセキュリティ監視および攻撃ログ記録。SQL インジェクション、
XSS、RCE、ディレクトリトラバーサル、ボットスキャナー、および /wp-admin 形式の偵察プローブを検出してログに記録します —
悪意のあるリクエストはすべて、完全なアプリケーションコンテキストとともにデータベースに記録されます。
これは IDS であり、WAF ではありません。リクエストをブロック、フィルタリング、変更することは一切ありません。
このような表示を見てここに来ましたか?
GET /wp-admin/setup-config.php 404 — on a site that isn't WordPress
GET /.env 404 — someone wants your database password
GET /?id=1' UNION SELECT password FROM 200 — SQL injection against a real route
GET /phpmyadmin/index.php 404 — scanning for an admin panel
そうしたリクエストはすでにあなたのLaravelアプリに届いている。アクセスログにはURLとステータスコードしか表示されておらず、それ以外は何も分からない — デコードされたペイロードも、どのルートが標的にされたのかも、同じIPがこの1時間に他に40件も試行したかどうかも。
このパッケージはそうした疑問に答える。Laravel 10〜13の任意のアプリに導入すれば、すべてのHTTPリクエストを150以上の攻撃パターンと照合してスキャンし、各マッチを信頼度でスコアリングしてデータベースに書き込む — 組み込みダッシュボード、Slackアラート、地理情報の付加、fail2ban/ブロックリストのエクスポート付きで。リクエストがブロックされることは一切ない。錠前ではなく防犯カメラだと考えてほしい。誰が、どれくらいの頻度で、どんな手法であなたのルートを探っているのかを正確に示してくれる。
本番アプリから抽出し、実際のトラフィックで実戦検証済み。2,400以上のテスト、Laravel本体以外に実行時依存なし、検出にインターネット接続も不要。
アップグレードする場合は UPGRADING.md を参照。コントリビュートする場合は CONTRIBUTING.md を参照。
1分以内に始める
composer require jayanta/laravel-threat-detection
php artisan vendor:publish --tag=threat-detection-migrations
php artisan migrate
次に、ミドルウェアを web グループに追加します(Laravel 11+ では bootstrap/app.php に1行、Laravel 10 では app/Http/Kernel.php に1行)— 完全なスニペットは下記のクイックスタートにあります。以上で検出が有効になります。
php artisan threat-detection:doctor # confirms it is actually recording
どこに位置づけられるか: IDS vs WAF vs エッジ
このパッケージはパッシブなアプリケーション層 IDS です — 監視して記録するだけで、 ブロックはしません。WAF やエッジサービスの代わりではなく、それらと並んで配置される ことを意図しています。各レイヤーは他には見えないものを見ています:
| このパッケージ (アプリ IDS) | WAF (ModSecurity + OWASP CRS, Cloudflare WAF) | エッジ / CDN (Cloudflare) | |
|---|---|---|---|
| 悪意のあるリクエストをブロック | ❌ ログのみ | ✅ | ✅ |
| 完全なアプリコンテキスト (正確な URL、デコード済みペイロード、認証済みユーザー) | ✅ | ⚠️ 部分的 | ❌ |
| 組み込みダッシュボード + あなたの DB 内の脅威ログ | ✅ | ⚠️ さまざま | ⚠️ エッジのみ |
| アプリ固有の検出 (例: Aadhaar / PAN / IFSC PII) | ✅ 組み込み + カスタム | ⚠️ 独自ルールを書く | ⚠️ カスタムルール、プラン依存 |
| オフライン / 外部サービス不要で動作 | ✅ | ⚠️ 依存 | ❌ |
| サーバーに到達する前にトラフィックを止める | ❌ | ⚠️ クラウド WAF のみ | ✅ |
| セットアップ | composer require、マイグレーション、ミドルウェア 1 行 | 中〜高 | 低〜中 |
| コスト | 無料、MIT | さまざま | 無料枠 + 有料 |
要するに: エッジ/WAF はドアの鍵であり、これは内側の防犯カメラです。 どのルートで、誰が、どれくらいの頻度で何を試みているかを正確に伝えるアプリ コンテキストを備えています。エッジレイヤーが決して見ることのないデータで、 実際の意思決定 — fail2ban の BAN、レート制限、ジオブロッキング — を支える ために使ってください。
意図的にそうではないもの
- WAF ではありません。 リクエストをブロック、フィルタリング、変更することは 決してありません。強制には Cloudflare、mod_security、または本物の WAF を 使ってください。(引き渡す先のエッジレイヤーがない場合は、 オペレーター側ヘルパーが パッケージの判断を公開するので、独自の 5 行のブロッキングミドルウェアを 書けます — 強制コードはパッケージのものではなく、あなたのものです。)
- セキュアコーディングの代替ではありません。 パラメータ化クエリ、入力検証、 出力エスケープが実際の防御です。このパッケージはあなたのコードがすでに 安全であることを前提とし、可視性を与えるものであって、保護ではありません。
- エッジサービスではありません。 Cloudflare を前に置けるならそうしてください — そしてエッジサービスが見られないアプリケーション層の詳細のためにこれを追加 してください。
- 完全な検出器ではなく、そうなることはできません。 パターンマッチングは 既知の攻撃に似た攻撃を捕捉します。新しい手法、あるいは十分に書き換えられた 見慣れた手法は、ログに残らず通過します — そして通過したことを知らされることも ありません。ここでの沈黙は「何もマッチしなかった」を意味し、「何も起こらな かった」を決して意味しません。その価値が発揮されるのは、公開された Laravel アプリに実際に到達するものの大半を占める、大量で低労力のトラフィックです: スキャナー、偵察プローブ、既製のインジェクション文字列、クレデンシャル スプレー。静かなログは、証拠の不在として扱い、不在の証拠として扱わないで ください。
初日から自分のコンテンツを検知すると想定してください
チューニングされていないインストールは正当なコンテンツで発火します。それは
インストール後にではなく、インストール前に知っておくべきことです。これらは
仮定ではなく実測値です。以下の行は
LegitimateTrafficCorpusTest
によって固定された下限のサンプルです。各行はそのリクエストがログに記録する
すべてを列挙しており、
ReadmeNoiseFloorTestは
いずれかの行がスイートの測定値と一致しなくなるとビルドを失敗させます。
| 完全に正当なリクエスト | チューニングされていないインストールがログに記録するもの |
|---|---|
検索ボックスに how to write a UNION SELECT in postgres と入力 | SQL Injection UNION / high |
<script>window.dataLayer=[];</script> を含むブログ記事 | XSS Script Tag / high · Command Chain Injection / medium |
貼り付けられた SELECT * FROM users WHERE id = 1 エラーを含むサポートチケット | SQLi Variant / high · SQL SELECT Query / low |
../../etc/passwd が古典的なトラバーサルペイロードであることを説明するドキュメント | Directory Traversal / medium · Sensitive File Access / medium |
| 本物のインドの携帯番号と PAN を収集するプロフィールフォーム | PAN Number Detected / high · Bank Account Number Detected / high · Mobile Number Detected / low |
これらはどれもバグではありません。 <script> を含むブログ記事は、バイト単位で
見れば保存型 XSS ペイロードです。UNION SELECT の検索は、その試みと区別が
つきません。10 桁の携帯番号は、もっともらしい銀行口座番号でもあります。
アプリケーションコンテキスト以外にこれらを区別するものはなく、どんなパターン
エンジンもそのコンテキストをあなたに代わって提供することはできません。
それを提供するのは 1 行の設定変更です: そのようなコンテンツを正当に運ぶフィールドを
safe_fields で指定する (またはネストされた JSON 内の 1 つのパスなら safe_paths)
と、それはもはやスキャンされません。それが上記の行を静めるスイッチです — そして
それは実際のトレードオフです。なぜなら、そのフィールドを通じて届けられる攻撃も
見えなくなるからです。
content_paths と relaxed モードは別のツールです。これらは低および中重大度の
ノイズを落とし、高重大度のマッチは意図的に記録し続けます — そのため設計上、
この表の high の行を静めることはありません。どちらをいつ使うかは
誤検知の削減を参照してください。
あなたのアプリがリッチテキスト、コードサンプル、または検索クエリを受け入れる なら、出力を判断する前にそうしてください。 デフォルトは、静かで不完全である よりも、意図的にノイズが多く正直であるようにしています: 既知のマッチを静める 方が、決して発火しなかったマッチを発見するよりも簡単だからです。
では実際にどう使うのか?
決してブロックしない検出器について最もよくある質問です。労力の少ない順に 4 つの答えがあります: