アップデート一覧に戻る
New releaseSep 3, 2026

laravel-threat-detection v1.7.2

SQLインジェクション、XSS、RCE、ボットスキャナー、175以上の攻撃パターンを検出・記録するパッシブなLaravelミドルウェア。内蔵ダッシュボード、Slackアラート、REST API、地理情報エンリッチメントを搭載。WAFではなくIDS。

共有

Latest Version Tests PHPStan Level 5 Code Style Pint Total Downloads PHP Version License

Laravel Threat Detection

Laravel 向けのセキュリティ監視・攻撃ログ記録。SQL インジェクション、XSS、RCE、ディレクトリトラバーサル、ボットスキャナー、/wp-admin 系の偵察プローブを検出して記録します。すべての敵対的リクエストは、完全なアプリケーションコンテキストとともにデータベースに記録されます。これは IDS であり WAF ではありません。リクエストをブロック、フィルタリング、変更することは一切ありません。

パッケージをインストールし、3 つの攻撃(SQL インジェクション、ディレクトリトラバーサル、XSS)を送信すると、ブロックされないためすべて HTTP 200 が返り、3 つすべてが threat-detection:stats にすでにカウントされています

このようなものを見たためにここに来たのですか?```

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/ブロックリストのエクスポートも備えています。リクエストがブロックされることは一切ありません。鍵ではなく防犯カメラと考えてください。誰がどのルートを探っているのか、どのくらいの頻度で、どのような手法を使っているのかを正確に示してくれます。

> 本番アプリから抽出し、実際のトラフィックで実戦テスト済み。335件のテスト、Laravel本体以外の実行時依存関係なし、検出にインターネット接続も不要です。
>
> アップグレードする場合? [UPGRADING.md](https://github.com/jay123anta/laravel-threat-detection/blob/main/UPGRADING.md) を参照してください。貢献する場合? [CONTRIBUTING.md](https://github.com/jay123anta/laravel-threat-detection/blob/main/CONTRIBUTING.md) を参照してください。

## 1分以内で始める```bash
composer require jayanta/laravel-threat-detection
php artisan vendor:publish --tag=threat-detection-migrations
php artisan migrate

次に、web グループにミドルウェアを追加します(Laravel 11+ では bootstrap/app.php、Laravel 10 では app/Http/Kernel.php の1行)— 完全なスニペットは下の クイックスタート にあります。 これで完了です。検出が有効になります。```bash php artisan threat-detection:doctor # confirms it is actually recording

---

## 位置づけ: IDS vs WAF vs エッジ

このパッケージは**パッシブなアプリケーションレベルのIDS**です。監視・記録は行いますが、ブロックはしません。WAFやエッジサービスの*横に*置くことを想定しており、置き換えるものではありません。各レイヤーは、他では見えないものをそれぞれ捉えます:

| | **このパッケージ** (アプリIDS) | **WAF** (mod_security、Cloudflare WAF) | **エッジ / CDN** (Cloudflare) |
|---|:---:|:---:|:---:|
| 悪意のあるリクエストをブロック | ❌ ログのみ | ✅ | ✅ |
| 完全なアプリコンテキスト (正確なルート、デコード済みペイロード、認証済みユーザー) | ✅ | ⚠️ 部分的 | ❌ |
| 組み込みダッシュボード + DB内の脅威ログ | ✅ | ⚠️ 場合による | ⚠️ エッジのみ |
| アプリ固有の検出 (例: Aadhaar / PAN / IFSC PII) | ✅ カスタムパターン | ❌ | ❌ |
| オフライン動作 / 外部サービス不要 | ✅ | ⚠️ 依存する | ❌ |
| アプリに到達する前にトラフィックを停止 | ❌ | ✅ エッジ | ✅ |
| セットアップ | `composer require` 1回 | 中〜高 | 低〜中 |
| コスト | 無料、MIT | 場合による | 無料枠 + 有料 |

**要約:** エッジ/WAFはドアの鍵、これは*内部*の防犯カメラであり、どのルートに対して何が、誰によって、どのくらいの頻度で試行されているかをアプリコンテキストで正確に教えてくれます。fail2banのBAN、レート制限、ジオブロッキングといった実際の判断を、エッジレイヤーでは決して見えないデータで下すために使用します。

### 意図的にそうではないもの

- **WAFではない。** リクエストをブロック、フィルタリング、変更することは一切ありません。強制にはCloudflare、mod_security、または本物のWAFを使用してください。(引き渡すエッジレイヤーがない場合? [オペレーター側ヘルパー](#acting-on-the-data-operator-side-blocking)がパッケージの判断を公開するので、独自の5行のブロッキングミドルウェアを書けます — 強制コードはパッケージではなく、あなたのもののままです。)
- **安全なコーディングの代替ではない。** パラメータ化クエリ、入力検証、出力エスケープが実際の防御です。このパッケージはコードがすでに安全であることを前提とし、*可視性*を提供します。保護ではありません。
- **エッジサービスではない。** Cloudflareを前面に置けるなら置いてください — その後、エッジサービスでは見えないアプリケーションレベルの詳細のためにこれを追加します。

### では、実際に何に使うのか?

ブロックしない検出器に関する最も一般的な質問。労力の少ない順に4つの答え:

| やりたいこと | 使用 | 労力 |
|---|---|---|
| 何が自分にヒットしているか確認 | [ダッシュボード](#dashboard) または `threat-detection:stats` | なし、すでに実行中 |
| ファイアウォールで常習犯をBAN | [`threat-detection:export-fail2ban`](#artisan-commands) — cronにパイプ | 1行 |
| Webサーバーで拒否 | [`threat-detection:export-blocklist`](#artisan-commands) → nginx/apacheディレクティブ | 1行 |
| アプリ内でリクエストを拒否 | [オペレーター側ヘルパー](#acting-on-the-data-operator-side-blocking) — `isBlocklisted()`、`isDdosThresholdExceeded()` | 独自ミドルウェア約10行 |
| リアルタイムで反応 | [`ThreatDetected` イベント](#threatdetected-event) — Telegram、SIEM、PagerDuty | リスナー |

パッケージはインテリジェンスを提供し、拒否はあなたが提供します。この分割は意図的です — アプリ内に存在する強制コードは、読み取り、テスト、無効化が可能なコードであり、検出バグがサイトをダウンさせることは決してないことを意味します。

### 他のLaravelセキュリティパッケージとの比較

これらは異なる問題を解決し、うまく組み合わせられます — この表は優劣ではなく、適切なツールを選ぶためのものです。

| パッケージ | 機能 | ブロック? | 使用する場面 |
|---|---|:---:|---|
| **このパッケージ** | 150以上のパターンに対してすべてのリクエストをスキャンし、完全なアプリコンテキストでログ記録 | ❌ | アプリに対して何が試行されているかを*確認*したい場合 |
| `spatie/laravel-honeypot` | スパムボットを捕捉する隠しフォームフィールド | ✅ フォームのみ | スパムされる公開フォームがある場合 |
| `graham-campbell/security` | 入力からXSS的なマークアップを除去 | ✅ 変更する | 単純な入力サニタイズが必要な場合 |
| `spatie/laravel-csp` | Content-Security-Policyヘッダーを送信 | ✅ ブラウザ | ブラウザが読み込むものを制限したい場合 |
| `laravel/fortify` + レート制限 | 認証スロットリングとロックアウト | ✅ | ログインにブルートフォース保護が必要な場合 |
| Cloudflare / mod_security | エッジWAF、アプリの前にブロック | ✅ | トラフィックが到着する前に停止させたい場合 |

正直なまとめ: ハニーポットはフォームスパムを捕捉し、WAFはエッジで既知の悪意トラフィックをブロックし、CSPはブラウザを制約します。**それらのどれも、攻撃者が特定のルートに対して何を試みたか、ペイロードがデコードされ認証済みユーザーが添付された状態で教えてくれるものはありません。** そのギャップを埋めるのがこれです — そして、パッケージが意図的にブロックしない理由でもあります: 上記のすべてと並行して、互いに競合することなく実行できます。

---

## 要件

- PHP 8.2+ (Laravel 13ではPHP 8.3+が必要)
- Laravel 10.x、11.x、12.x、または13.x
- Laravelがサポートする任意のデータベース (MySQL、PostgreSQL、SQLite、SQL Server)
- 任意のキャッシュドライバー — **Redisやキューワーカーは不要**。Redis/Memcachedは、オプションのDDoSチェックを有効にするために*推奨*されるだけです (非アトミックドライバーでは自動無効化)。キュー書き込みはオプトインで、デフォルトではオフです。

---

## 仕組み

1. ミドルウェアがすべての受信HTTPリクエストをスキャン
2. リクエストはSQLインジェクション、XSS、RCE、ファイルトラバーサル、SSRF、LDAP、XPath、SSTIなどをカバーする158の正規表現パターンに対してチェック
3. 脅威パターンが一致した場合、IP、URL、脅威タイプ、重大度レベル、信頼スコアを含むレコードが`threat_logs`データベーステーブルに書き込まれる
4. オプションで、高重大度の脅威に対してSlackアラートが送信される
5. リクエストは通常どおり続行 — **何もブロックされない**

検出にインターネット接続は不要です。

---

## クイックスタート

### 1. パッケージをインストール```bash
composer require jayanta/laravel-threat-detection

2. マイグレーションを公開して実行する

この手順は必須です。 これを実行しないと、パッケージは脅威を検出できてもデータベースに保存できません。この手順をスキップすると、threat_logs テーブルが存在せず、すべての検出結果が静かに失われます(storage/logs/laravel.log にエラーのみが表示されます)。```bash php artisan vendor:publish --tag=threat-detection-migrations php artisan migrate

これにより、`threat_logs`(検出された脅威を保存)と`threat_exclusion_rules`(誤検知ルールを保存)の2つのテーブルが作成されます。

**テーブルが作成されたことを確認する:**```bash
php artisan migrate:status

create_threat_logs_tableadd_confidence_to_threat_logs_tablecreate_threat_exclusion_rules_table を探してください。すべて Ran と表示されるはずです。

3. ミドルウェアを登録する

ミドルウェアはリクエストをスキャンするものです。これを web ミドルウェアグループに追加する必要があります。

Laravel 11 または 12 を使用している場合 - bootstrap/app.php を開きます:```php ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ \JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class, ]); })

> **Laravel のバージョンを確認する方法:** ターミナルで `php artisan --version` を実行します。

**Laravel 10 を使用している場合** - `app/Http/Kernel.php` を開きます:```php
protected $middlewareGroups = [
    'web' => [
        // ... existing middleware
        \JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class,
    ],
];

4. (任意)設定ファイルを公開する```bash

php artisan vendor:publish --tag=threat-detection-config

このパッケージは適切なデフォルト設定で動作します。設定を公開すると、検出パターン、感度モード、Slack通知などをカスタマイズできます。この手順をスキップしても、すべて正常に動作します。

**これで完了です。** アプリは脅威を検出できるようになりました。

---

## 動作確認

インストール後、テスト用の脅威をトリガーして、ログに記録されたことを確認します。

### ステップ1: アプリを起動する```bash
php artisan serve

ステップ2: ブラウザでテストURLを開く

アプリ内の既存の任意のルート(ホームページ、商品ページなど)に悪意のあるクエリパラメータを追加します。例:

SQLインジェクション:``` http://localhost:8000/?q=' UNION SELECT * FROM users--

**XSS(クロスサイトスクリプティング):**```
http://localhost:8000/?q=<script>alert(1)</script>

ディレクトリトラバーサル:``` http://localhost:8000/?file=../../etc/passwd

**RCE(リモートコード実行):**```
http://localhost:8000/?cmd=system('ls -la')

Shellshock (CVE-2014-6271):``` http://localhost:8000/?cmd=() { :;}; /bin/bash

**Windows コマンドインジェクション:**```
http://localhost:8000/?cmd=powershell -c whoami

DROP TABLE(SQL DDL):``` http://localhost:8000/?q=DROP TABLE users

> アプリ内に実際に存在するルート(`/` など)を使用してください。URL が 404 を返す場合、ミドルウェアが実行されていない可能性があります。

### ステップ 3: 脅威がログに記録されたことを確認する

**オプション A - Artisan コマンド(最速):**```bash
php artisan threat-detection:stats

Total Threats の表、重大度のカウント、上位IPが表示されます。

オプションB - Tinker:```bash php artisan tinker

```php
DB::table('threat_logs')->latest()->take(5)->get(['ip_address', 'type', 'threat_level', 'confidence_score']);

オプションC - Laravelログファイル: 検出された各脅威は、警告としてstorage/logs/laravel.logに書き込まれます:``` [high] Threat Detected: [middleware] SQL Injection UNION from 127.0.0.1 (http://localhost:8000/?q=...) [confidence: 50%]

### テスト時の注意点

| 動作 | 説明 |
|----------|-------------|
| 同じ脅威は5分間に1回のみログ記録 | 重複排除: 同じIP + 同じ脅威タイプは5分間キャッシュされます。各テストでは**異なる攻撃タイプ**を使用するか、テスト間で待機してください。 |
| `curl` リクエストは追加の検出をトリガー | `curl` を使用すると、「cURL Command」ユーザーエージェント検出(低重要度)もログに記録されます。これは想定内です。パッケージは自動化ツールを検出します。 |
| パッケージはリクエストをブロックしない | アプリは正常に機能し続けます。検出は受動的です。 |
| Slackの設定は不要 | 通知はデフォルトでオフです。 |
| インターネット接続は不要 | コア検出は100%ローカルです。オプションの `threat-detection:enrich` コマンドのみが、地理位置情報データのために外部APIを呼び出します。 |

### トラブルシューティング

**ここから始めてください — 1つのコマンドでほとんどの問題が解決します:**```bash
php artisan threat-detection:doctor

検出が静かに失敗する原因をチェックします。ダッシュボードが空のままになる状況は「攻撃なし」と見た目が同じですが、そのようなケースを検出し、それぞれの正確な修正方法を出力します。実際の失敗時には非ゼロの終了コードを返すため、CIやデプロイステップで安全に実行できます。``` Threat Detection — health check

PASS Detection is enabled for this environment FAIL 'threat_logs' is missing confidence_label — EVERY threat is being discarded Run: php artisan vendor:publish --tag=threat-detection-migrations && php artisan migrate WARN 1 custom pattern(s) shadow a built-in: Localhost SSRF Your copy runs instead of the maintained one, so later fixes to it never reach you.

対象範囲: この環境で有効化されている検出機能。ライターが必要とするすべてのカラム(1つ欠けると**すべての**脅威が破棄される)。ダッシュボード/APIカラム。除外ルールのテーブル。ミドルウェアが実際にルートまたはグループに配線されているかどうか。このバージョンより前に公開された設定。組み込みパターンを覆い隠すカスタムパターン。DDoSカウントを実行できないキャッシュドライバ。認証なしで開かれたままのダッシュボードまたはAPI。

**「テストしたのに `threat-detection:stats` がゼロ件の脅威を表示する」/「脅威がデータベースに保存されない」**

ドクターが合格した場合、インストールは正常で、問題はテストリクエスト自体にあります。ドクターが確認できない3つの項目:

| チェック | 確認方法 |
|-------|---------------|
| IPがホワイトリストに登録されていない | `.env` に `THREAT_DETECTION_WHITELISTED_IPS` を追加した場合は、テスト中に削除する |
| 既存のルートを使用した | テストURLは実際のルート(例: `/`)と一致する必要がある。404が返る場合、ミドルウェアは実行されていない |
| 重複排除キャッシュ | 同じIP + 同じ攻撃タイプは5分間キャッシュされる - 別の攻撃タイプで試す |

> `php artisan migrate` を単独で実行するだけでは不十分です。マイグレーションファイルはパッケージ内にあり、最初にアプリの `database/migrations/` に公開する必要があります。この問題が原因の場合、ドクターが正確なコマンドを表示します。

**「APIが401 Unauthorizedを返す」**

以下の [API認証](#api-authentication) を参照してください。

**「ダッシュボードが404を表示する」**

ダッシュボードはデフォルトで無効です。`.env` に `THREAT_DETECTION_DASHBOARD=true` を追加し、ルートキャッシュをクリアしてください:```bash
php artisan route:clear

機能

  • 150以上の検出パターン - SQLインジェクション(UNION、DDL、DML、ファイル操作)、XSS(script、SVG、CSS式)、RCE、ディレクトリトラバーサル、SSRF、XXE、Log4Shell、NoSQLインジェクション、コマンドインジェクション(Linux + Windows)、LDAPインジェクション、XPathインジェクション、SSTI、CRLFインジェクション、Javaデシリアライゼーションなど
  • 83のボット/スキャナーシグネチャ - SQLMap、Nikto、Nmap、Burp Suite、FeroxBuster、FFUF、XSStrike、Dalfox、Netsparker、およびその他70以上のスキャナーおよびボットシグネチャ
  • AIスクレイパー検出 - GPTBot、ClaudeBot、ByteSpider、Common Crawl、およびその他のAIトレーニングボット
  • ヘッドレスブラウザ検出 - HeadlessChrome、PhantomJS、Selenium、Puppeteer、Playwright
  • 404プローブ追跡 - 既知の脆弱なパス(/wp-admin/.env/phpmyadmin/actuator など)への偵察プローブを検出(50以上のデフォルトプローブパス)
  • DDoS監視 - 設定可能なウィンドウによるレートベースのしきい値検出
  • 信頼度スコアリング - 各脅威は、パターン数、コンテキスト、シグナルに基づいて0〜100の信頼度スコアを取得
  • 回避耐性 - 正規化パイプラインにより、パターンマッチング前にSQLコメント挿入、二重URLエンコーディング、HTMLエンティティエンコーディング、Unicodeエスケープ、16進エスケープを無効化
  • CVE検出 - Shellshock(CVE-2014-6271)、Spring4Shell(CVE-2022-22965)、PHPUnit RCE(CVE-2017-9841)、Drupalgeddon、Log4Shell
  • コンテキスト認識検出 - クエリ文字列で見つかったパターンは、リクエストボディ内のものよりも高いスコアを獲得
  • リクエストボディスキャン - フォームエンコードおよびJSON(application/json)の両方のリクエストボディが検査される
  • セーフフィールド - スキャンから特定のフォームフィールドを除外(CMSエディタ、コード入力、検索フィールド用)
  • 誤検知レポート - ダッシュボードから脅威を誤検知としてマーク。除外ルールを自動生成
  • 3つの検出モード - strictbalanced(デフォルト)、および relaxed - 感度を調整可能
  • コンテンツパス抑制 - CMS/ブログパスをホワイトリストに登録し、リッチコンテンツからの低/中アラートを抑制
  • PII検出 - 機密データ露出パターン(地域ごとに設定可能)
  • ジオエンリッチメント - 無料APIによる国、都市、ISP、クラウドプロバイダーの識別
  • Slackアラート - 高重大度の脅威に対するリアルタイム通知(Laravel 10および11+で動作)
  • 組み込みダッシュボード - ダークモードのBladeダッシュボード(Alpine.js + Tailwind CDN、ビルドステップ不要)
  • ダッシュボード認証ガード - ダッシュボードおよびAPI用の設定可能な認証(なし、auth、role、またはIPベース)
  • 15のAPIエンドポイント - カスタムVue/React/モバイルダッシュボード構築用の完全なREST API
  • Fail2banエクスポート - 検出されたIPをfail2ban互換形式またはプレーンなブロックリストでエクスポート
  • ブロックリストエクスポート - IPをnginx deny、Apache deny、CSV、またはプレーン形式でエクスポート
  • CSVエクスポート - ワンクリックの脅威ログエクスポート(最大10,000行)
  • 相関分析 - IP全体での協調攻撃および攻撃キャンペーンを検出
  • パフォーマンス最適化 - カテゴリベースの遅延パターン読み込み(関連する攻撃カテゴリに対してのみ正規表現を実行)、クリーンなリクエストに対する早期脱出、ブラウザUAショートサーキット(通常のブラウザでは70以上のチェックをスキップ)、プローブパスのハッシュルックアップ、バッチDB挿入、リクエストごとの設定可能な最大検出数
  • データベース非依存 - MySQL、PostgreSQL、SQLite、SQL Server
  • ゼロ設定 - 適切なデフォルトでそのまま動作
  • 設計上の安全性 - ミドルウェアは自身のエラーを捕捉します。検出が失敗しても、アプリは動作を継続します。リクエストがブロックされることはありません。

設定

このパッケージは、.env の変更なしで動作します。以下のすべての値はオプションです。デフォルトを上書きする場合のみ追加してください。```env

Enable/disable detection globally (default: true)

THREAT_DETECTION_ENABLED=true

Detection sensitivity (default: balanced)

Options: strict, balanced, relaxed

THREAT_DETECTION_MODE=balanced

Custom table name (default: threat_logs)

THREAT_DETECTION_TABLE=threat_logs

Your ISO 3166-1 alpha-2 country code (default: IN)

Drives the is_foreign flag on every enriched row — set this or every

non-Indian address is reported as foreign.

THREAT_DETECTION_HOME_COUNTRY=IN

Geo-enrichment provider used by threat-detection:enrich (default shown).

Cleartext HTTP because ip-api.com's free tier rejects HTTPS; point this at

an HTTPS endpoint if you hold a key. Enrichment is opt-in either way.

THREAT_DETECTION_GEO_ENDPOINT=http://ip-api.com/json

Dashboard URL path (default: threat-detection)

THREAT_DETECTION_DASHBOARD_PATH=threat-detection

API route prefix (default: api/threat-detection)

THREAT_DETECTION_API_PREFIX=api/threat-detection

Role required when the API guard is 'role' (default: admin)

THREAT_DETECTION_API_ROLE=admin

Allowed IPs when the API guard is 'ip'. Comma-separated, CIDR supported.

THREAT_DETECTION_API_IPS=127.0.0.1,10.0.0.0/8

Username shown on Slack alerts (default: ThreatBot)

THREAT_DETECTION_SLACK_USERNAME=ThreatBot

Whitelist IPs to skip detection entirely (default: empty)

Supports CIDR notation. Comma-separated.

THREAT_DETECTION_WHITELISTED_IPS=10.0.0.0/8,192.168.1.0/24

Static operator denylist read by ThreatDetection::isBlocklisted() (default: empty)

The package itself never blocks — see "Acting on the Data" for the

enforcement recipe. Supports CIDR. Whitelist wins on overlap.

THREAT_DETECTION_BLOCKLISTED_IPS=203.0.113.0/24,198.51.100.7

DDoS detection thresholds (defaults shown)

THREAT_DETECTION_DDOS_THRESHOLD=300

THREAT_DETECTION_DDOS_WINDOW=60

Minimum confidence score to log a threat (default: 0)

Threats below this score are silently ignored.

THREAT_DETECTION_MIN_CONFIDENCE=0

Slack notifications (disabled by default)

THREAT_DETECTION_NOTIFICATIONS=true

THREAT_DETECTION_SLACK_WEBHOOK=https://hooks.slack.com/services/YOUR/WEBHOOK/URL

THREAT_DETECTION_SLACK_CHANNEL=#threat-alerts

Dashboard (disabled by default)

THREAT_DETECTION_DASHBOARD=true

API endpoints (enabled by default)

THREAT_DETECTION_API=true

API rate limiting (default: 60 requests per minute)

THREAT_DETECTION_API_THROTTLE=60,1

Queue support - offload DB writes to a queue (disabled by default).

OPTIONAL: only enable if your app already runs a queue worker. When false

(default), threats are written synchronously with a plain DB insert - no

Redis, no worker, nothing extra to run.

THREAT_DETECTION_QUEUE=false

THREAT_DETECTION_QUEUE_CONNECTION=redis

THREAT_DETECTION_QUEUE_NAME=default

Auto-purge old logs (disabled by default)

Requires Laravel scheduler to be running.

THREAT_DETECTION_RETENTION=false

THREAT_DETECTION_RETENTION_DAYS=90

404 probe tracking (enabled by default)

Detects bots hitting /wp-admin, /.env, /phpmyadmin, etc.

THREAT_DETECTION_PROBE_TRACKING=true

Max detections per request (default: 0 = unlimited)

Stop scanning after N pattern matches per request.

THREAT_DETECTION_MAX_DETECTIONS=0

Dashboard auth guard (default: none)

Options: none, auth, role, ip

THREAT_DETECTION_DASHBOARD_GUARD=none

THREAT_DETECTION_DASHBOARD_ROLE=admin

THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1

API auth guard (default: none - uses existing middleware config)

THREAT_DETECTION_API_GUARD=none

### 検出モード

| モード | 信頼度しきい値 | 動作 |
|------|---------------------|----------|
| `strict` | 0(すべてをログ記録) | すべてのパターンが有効、最低しきい値。すべてを検出するが、正当なトラフィックをフラグ付けする可能性がある。 |
| `balanced` | 10 | デフォルト。信頼度スコアリングが有効、標準しきい値。ほとんどのアプリに適している。 |
| `relaxed` | 40 | 高重大度パターンのみがトリガーされる。誤検知が頻発するコンテンツ重視のサイトに最適。 |

### 有効な環境

デフォルトでは、検出は `production`、`staging`、`local` で実行されます。変更するには、設定を公開して編集します:```php
'enabled_environments' => ['production', 'staging', 'local'],

テストスイートで検出を無効にするには、APP_ENV=testing(上記のリストには含まれていません)を設定するか、phpunit.xmlに以下を追加します:```xml

### 設定リファレンス

設定ファイルを公開して、利用可能なすべてのオプションを確認します:```bash
php artisan vendor:publish --tag=threat-detection-config

主要な設定セクション: skip_paths(スキップするパス)、only_paths(ホワイトリストモード)、auth_paths(ログインルートのスマート検出)、content_paths(高重要度以外のアラートを抑制)、safe_fields(スキャンから特定フィールドを除外)、safe_paths(ネストされたJSONに対するパス認識型フィールド除外)、probe_tracking(404プローブ検出)、context_weights(スコアリング乗数)、threat_levels(深刻度キーワードのマッピング)、api_route_filtering(APIルートでの低/中重要度を抑制)、queue(非同期処理)、retention(自動パージ)、max_detections_per_request(パフォーマンス上限)、dashboard.guard / api.guard(認証モード)。

ルートのホワイトリスト化(only_paths)

アプリに多くのルートがあるが、一部のルートだけに関心がある場合は、only_pathsを使用してそれらのルートのみをスキャンします。他のすべてのルートは自動的にスキップされます - ミドルウェアのオーバーヘッドは一切ありません。```php // config/threat-detection.php 'only_paths' => [ 'admin/', 'api/', 'login', 'register', ],

Leave empty (default) to scan all routes (subject to `skip_paths`). When both are configured, `only_paths` is checked first, then `skip_paths` applies within the matched set.

### Queue Support

By default, threat logging happens synchronously in the request cycle. For high-traffic apps, you can offload DB writes and Slack notifications to a queue:```env
THREAT_DETECTION_QUEUE=true
THREAT_DETECTION_QUEUE_CONNECTION=redis
THREAT_DETECTION_QUEUE_NAME=threat-logs

This dispatches a StoreThreatLog job (3 retries, backoff 10s/30s). Detection still happens in real-time - only the write is deferred.

Auto-Purge (Retention Policy)

Automatically delete old threat logs on a daily schedule:```env THREAT_DETECTION_RETENTION=true THREAT_DETECTION_RETENTION_DAYS=90

Laravelのスケジューラーが実行されている必要があります(`php artisan schedule:run`)。`threat-detection:purge` により毎日02:00に実行されます。

### ThreatDetected イベント

確認された脅威はそれぞれ `ThreatDetected` イベントをディスパッチします。これをリッスンできます:```php
// app/Providers/EventServiceProvider.php
use JayAnta\ThreatDetection\Events\ThreatDetected;

protected $listen = [
    ThreatDetected::class => [
        YourCustomListener::class,
    ],
];

The event carries $threatLog (full DB row array), $ipAddress, and $threatLevel. Use it to trigger custom actions - send Telegram alerts, update a blocklist, feed a SIEM, etc.

DdosThresholdExceeded Event

When a client crosses the configured DDoS threshold (ddos.threshold requests within ddos.window seconds), a DdosThresholdExceeded event is dispatched alongside the threat log entry:```php use JayAnta\ThreatDetection\Events\DdosThresholdExceeded;

protected $listen = [ DdosThresholdExceeded::class => [ YourFloodListener::class, ], ];

イベントは `$ipAddress`、`$requestCount`、`$threshold`、`$windowSeconds` を保持します。これはIPごとに重複排除ウィンドウごとに1回に制限されるため(ログ行と同じスロットル)、フラッド攻撃でリスナーが埋もれることはありません。アラート通知や外部の禁止ストアへの供給に使用してください。閾値を超えたクライアントを*拒否*するには、代わりに独自のミドルウェアから `ThreatDetection::isDdosThresholdExceeded($ip)` を使用してください — [データへの対応](#acting-on-the-data-operator-side-blocking) を参照してください。

---

## Slack通知

Slackアラートはデフォルトで無効です。有効にするには:```env
THREAT_DETECTION_NOTIFICATIONS=true
THREAT_DETECTION_SLACK_WEBHOOK=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
THREAT_DETECTION_SLACK_CHANNEL=#threat-alerts

高重大度の脅威のみがデフォルトで通知をトリガーします(設定内の notify_levels で変更可能)。

Laravel 10: 組み込みの SlackMessage 通知クラスを使用します。追加のパッケージは不要です。

Laravel 11+: 組み込みの Slack チャンネルは削除されました。このパッケージはこれを自動的に検出し、生の HTTP POST ウェブフックを Slack URL に送信します。追加のパッケージは不要です。完全な通知チャンネルを希望する場合は、以下をインストールしてください:```bash composer require laravel/slack-notification-channel

---

## ダッシュボード

<p align="center">
  <img src="https://assets.kitploit.com/production/public/readmes/12500/fc7950bd0cc6323bcc2d62b03e31c99edc7450b0ef76f9cbfa5133b527b25269.png" alt="脅威検出ダッシュボード — 統計、7日間のタイムライン、ライブ脅威ログ、上位の攻撃元IP、国別の脅威" width="100%">
</p>

このパッケージには、ダークモード対応のダッシュボード(Alpine.js + Tailwind CDN — ビルド手順不要)が組み込まれています。```
+-------------------------------------------------------------------------+
|  Threat Detection Dashboard                                              |
+-------------------------------------------------------------------------+
|  Total: 847  |  High: 23  |  Med: 156  |  Low: 668  |  IPs: 94         |
+-------------------------------------------------------------------------+
|  [Timeline Chart - 7 Day Stacked Bar]                                   |
+-------------------------------------------------------------------------+
|  Search: [___________]  Level: [All]                                    |
|  Time         IP             Type            Level  Confidence  Actions  |
|  Mar 2 14:02  185.220.101.4  SQL Injection   HIGH   80%         [FP]    |
|  Mar 2 13:58  45.33.32.156   XSS Script Tag  HIGH   65%         [FP]    |
|  Mar 2 13:45  192.168.1.10   Scanner: Nikto  MED    35%         [FP]    |
+-------------------------------------------------------------------------+
|  Top IPs              |  Threats by Country                              |
|  185.220.101.4  [23]  |  US  234                                        |
|  45.33.32.156   [18]  |  CN  156                                        |
|  103.152.220.1  [12]  |  RU  98                                         |
+-------------------------------------------------------------------------+

ダッシュボードを有効にする

.env に追加:```env THREAT_DETECTION_DASHBOARD=true

訪問先: `http://your-app.test/threat-detection`

### ローカル開発中にアクセスする

ダッシュボードはデフォルトで `['web', 'auth']` ミドルウェアを使用するため、ユーザーはログインしている必要があります。アプリにまだ認証がない場合は、代わりに自分のマシンに限定してください:```env
THREAT_DETECTION_DASHBOARD_GUARD=ip
THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1

すべてのガードオプション、および検出を無効化するエンドポイント上の個別ガードについては、ダッシュボードとAPI認証で説明しています。

ダッシュボードに空のデータが表示される場合、ページは読み込まれましたが、そのAPI呼び出しは実行されていません。API認証を参照してください。


APIエンドポイント

このパッケージは、カスタムダッシュボードや統合を構築するための15個のRESTエンドポイントを提供します。

API認証

APIルートはデフォルトでauth:sanctumミドルウェアを使用します。パッケージはこれを適切に処理します:

  • Sanctumがインストールされている場合: APIはSanctumトークンまたはSPAセッション認証による認証を必要とします。
  • Sanctumがインストールされていない場合: パッケージはSanctumが存在しないことを自動的に検出し、['api']のみにフォールバックします。APIは認証なしで動作します。

Sanctumを使用しないがAPIを保護したい場合、2つのオプションがあります:

オプション1 - 組み込みの認証ガードを使用する:```env THREAT_DETECTION_API_GUARD=auth

**オプション 2 - ミドルウェアを直接変更する:**```php
// config/threat-detection.php
'api' => [
    'enabled' => true,
    'prefix' => 'api/threat-detection',
    'middleware' => ['api', 'auth'],  // or 'auth:your-guard'
],

ローカルテスト用(Sanctumがアクセスをブロックする場合)、一時的に以下を変更します:```php 'middleware' => ['api'], // remove 'auth:sanctum'

> 本番環境にデプロイする前に認証を復元してください。

### エンドポイントリファレンス

| メソッド | エンドポイント | 説明 |
|--------|----------|-------------|
| GET | `/api/threat-detection/threats` | 脅威の一覧(ページング、フィルタリング対応) |
| GET | `/api/threat-detection/threats/{id}` | 単一の脅威の詳細 |
| POST | `/api/threat-detection/threats/{id}/false-positive` | 脅威を誤検知としてマーク |
| GET | `/api/threat-detection/stats` | 全体の統計 |
| GET | `/api/threat-detection/summary` | タイプ、レベル、IP別の詳細な内訳 |
| GET | `/api/threat-detection/live-count` | 直近1時間の脅威 |
| GET | `/api/threat-detection/by-country` | 国別にグループ化 |
| GET | `/api/threat-detection/by-cloud-provider` | クラウドプロバイダー別にグループ化 |
| GET | `/api/threat-detection/top-ips` | 攻撃が多い上位IP |
| GET | `/api/threat-detection/timeline` | 脅威のタイムライン(チャート用) |
| GET | `/api/threat-detection/ip-stats?ip=x.x.x.x` | 特定のIPの統計 |
| GET | `/api/threat-detection/correlation` | 相関分析 |
| GET | `/api/threat-detection/export` | CSVにエクスポート |
| GET | `/api/threat-detection/exclusion-rules` | 除外ルールの一覧 |
| DELETE | `/api/threat-detection/exclusion-rules/{id}` | 除外ルールの削除 |

### `/threats` のクエリパラメータ

| パラメータ | 説明 |
|-----------|-------------|
| `keyword` | IP、URL、タイプ内を検索 |
| `ip` | IPアドレスでフィルタリング |
| `level` | 脅威レベルでフィルタリング(`high`、`medium`、`low`) |
| `type` | 脅威タイプでフィルタリング |
| `country` | 国コードでフィルタリング |
| `is_foreign` | 海外IPでフィルタリング(`true`/`false`) |
| `cloud_provider` | クラウドプロバイダーでフィルタリング |
| `is_false_positive` | 誤検知ステータスでフィルタリング(`true`/`false`) |
| `date_from` / `date_to` | 日付範囲フィルタ |
| `per_page` | 1ページあたりの件数(デフォルト: 20、最大: 100) |

### APIレスポンスの例

**GET `/api/threat-detection/stats`:**```json
{
  "success": true,
  "data": {
    "total_threats": 847,
    "high_severity": 23,
    "medium_severity": 156,
    "low_severity": 668,
    "unique_ips": 94,
    "foreign_ips": 67,
    "cloud_attacks": 12,
    "today": 34,
    "last_hour": 5
  }
}

カスタムフロントエンドの構築

Vue.js:```javascript async mounted() { const response = await fetch('/api/threat-detection/stats'); this.stats = await response.json();

const threats = await fetch('/api/threat-detection/threats?per_page=20');
this.threats = await threats.json();

}

**React:**```jsx
useEffect(() => {
    fetch('/api/threat-detection/stats')
        .then(res => res.json())
        .then(data => setStats(data));
}, []);

APIがauth:sanctumを使用している場合は、認証ヘッダーを含めるか、クッキーベースのリクエスト用にSanctum SPA認証を設定してください。


Artisanコマンド```bash

Check that detection is installed, wired up and actually recording.

Exits non-zero on a real failure, so it works in CI or a deploy step.

php artisan threat-detection:doctor

View threat stats summary in the terminal

php artisan threat-detection:stats

Enrich existing logs with geo-data (country, city, ISP, cloud provider)

Uses the free ip-api.com service (rate-limited to 45 req/min, auto-throttled)

php artisan threat-detection:enrich --days=7

Purge old logs to keep the database clean

php artisan threat-detection:purge --days=30

Export threat IPs for fail2ban (pipe to file or run directly)

php artisan threat-detection:export-fail2ban --level=high --since=24h --min-hits=5 php artisan threat-detection:export-fail2ban --format=plain > /tmp/banlist.txt

Export blocklist in various formats

php artisan threat-detection:export-blocklist --format=nginx > /etc/nginx/blocklist.conf php artisan threat-detection:export-blocklist --format=apache > .htaccess-deny php artisan threat-detection:export-blocklist --format=csv --since=7d

## データへの対応(オペレーター側でのブロック)

このパッケージはリクエストをブロックすることはありません。それがこのパッケージのアイデンティティであり、デフォルト動作ではありません。上記のエクスポートは、すでに運用している防御レイヤー(fail2ban、nginx、エッジWAF)にデータを供給します。しかし、一部のデプロイ環境にはそのような供給先となるレイヤーが存在しません。共有ホスティング、PaaS、制御できないロードバランサーの背後にあるコンテナなどです。そのような環境向けに、このパッケージは*判定結果*をヘルパーとして公開しており、強制(ブロック)のミドルウェアはご自身で記述します。エクスポートと同じアーキテクチャです。**インテリジェンスは当方が提供し、拒否の実行はお客様が提供します。**```php
// app/Http/Middleware/EnforceThreatDecisions.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use JayAnta\ThreatDetection\Facades\ThreatDetection;

class EnforceThreatDecisions
{
    public function handle(Request $request, Closure $next)
    {
        $ip = (string) $request->ip();

        // Static operator denylist (config: blocklisted_ips).
        // CIDR supported; whitelisted_ips wins on overlap.
        if (ThreatDetection::isBlocklisted($ip)) {
            abort(403);
        }

        // Volumetric flood: refuse over-threshold clients until the window resets.
        if (ThreatDetection::isDdosThresholdExceeded($ip)) {
            return response('Too Many Requests', 429, [
                'Retry-After' => (string) config('threat-detection.ddos.window', 60),
            ]);
        }

        return $next($request);
    }
}

IP で強制する前に、TrustProxies を設定してください。

上記のすべては $request->ip() に依存しています。ロードバランサー、CDN、または リバースプロキシの背後では、Laravel が信頼するプロキシを認識している場合にのみ、そのメソッドは クライアント IP を返します。 認識していない場合、2 つの問題が同時に発生します。すべてのリクエストが プロキシから来たように見えるため、拒否リストのエントリがすべてのトラフィックをブロックするか、まったくブロックしないかのどちらかになります。さらに悪いことに、アプリが信頼すべきでない 転送ヘッダーを信頼すると、攻撃者が X-Forwarded-For を設定して ブロックリストを簡単にすり抜けてしまいます。

これは whitelisted_ips の場合よりもここで重要です。ホワイトリストの誤った一致は スキップされた可能性のあるリクエストをパッケージがスキャンすることを意味するだけであり、フェイルセーフです。 トラフィックを拒否するために使用される拒否リストは オープン に失敗します。つまり、アドレスがブロックされていると 思っていても、実際にはブロックされていないということです。どちらのヘルパーを強制に使用する場合でも、その前に app/Http/Middleware/TrustProxies.php(または Laravel 11+ の bootstrap/app.php 内の trustProxies 呼び出し)を確認してください。

グローバルに登録します(検出ミドルウェアの前で問題ありません。ヘルパーは設定と キャッシュを読み取るだけで、ミドルウェアの順序には依存しないためです):```php // bootstrap/app.php (Laravel 11+) ->withMiddleware(function ($middleware) { $middleware->prepend(\App\Http\Middleware\EnforceThreatDecisions::class); })

ヘルパー:

| ヘルパー | 戻り値 | バックエンド |
|---|---|---|
| `ThreatDetection::isBlocklisted($ip)` | `bool` | `blocklisted_ips` 設定(`IpUtils` によるCIDR対応。ホワイトリストが優先) |
| `ThreatDetection::isWhitelisted($ip)` | `bool` | `whitelisted_ips` 設定 |
| `ThreatDetection::ddosRequestCount($ip)` | `int` | 検出ミドルウェアが維持するフラッドカウンター |
| `ThreatDetection::isDdosThresholdExceeded($ip)` | `bool` | そのカウンターと `ddos.threshold` の比較 |

注意点:

- **拒否リストは静的で、運用者が管理します。** パッケージ内でこれに追加することは一切ありません。これは fail2ban のジョイルが下すのと同じ判断(「ダッシュボードを確認した。この /24 は敵対的だ」)を、アプリ内で実行するだけです。
- DDoS カウンターは検出に到達したリクエストのみをカウントし(`skip_paths`、ホワイトリスト登録済みIP、無効化された環境は決してカウントされません)、DDoS 検出が無効なキャッシュドライバー(`file`、`database`、`null`)では 0 のままです。
- クライアントがしきい値を超えると、[`DdosThresholdExceeded` イベント](#ddosthresholdexceeded-event)もディスパッチされます。これはアラートや外部の禁止リストへの供給に役立ちます。ただし、リスナー内で `abort()` を呼び出さないでください。リスナーは検出ミドルウェアのフェイルオープンな `try/catch` 内で実行されるため、拒否は上記のように独自のミドルウェアで行うべきです。

---

## 404 プローブ追跡

このパッケージは偵察プローブを検出します。これは、WordPress や phpMyAdmin を使用していないサイトに対して `/wp-admin`、`/.env`、`/phpmyadmin` などの既知の脆弱なパスにアクセスするボットです。これらには悪意のあるペイロードはなく、パス自体がシグナルとなります。

ペイロードベースの検出とは別に、`[probe]` タイプタグで記録されます。プローブリクエストに悪意のあるペイロードも含まれている場合、両方が独立して記録されます。

デフォルトで有効で、50以上のプローブパスが含まれています。`config/threat-detection.php` でカスタマイズできます:```php
'probe_tracking' => [
    'enabled' => true,
    'default_level' => 'medium',
    'paths' => [
        '/wp-admin' => 'WordPress Admin',
        '/wp-admin/*' => 'WordPress Admin',
        '/.env' => 'Environment File',
        '/phpmyadmin' => 'phpMyAdmin',
        '/actuator/*' => 'Spring Actuator',
        // Add your own probe paths...
    ],
],

THREAT_DETECTION_PROBE_TRACKING=false で無効にします。


セーフフィールド(誤検知の低減)

特定のフォームフィールドに正当なHTML、SQLキーワード、またはコード(例:CMSエディタ、コードスニペット入力)が含まれる場合、それらをスキャンから除外できます:```php // config/threat-detection.php 'safe_fields' => ['content', 'body', 'html', 'description', 'code'],

ここにリストされているフィールドは、検出が実行される前にクエリパラメータとリクエストボディ(フォームエンコードおよびJSON(`application/json`)の両方)から取り除かれます。同じリクエスト上の他のフィールドは引き続き完全にスキャンされます。

### セーフパス(ネストされたJSON API向けのパス認識型)

`safe_fields` は、キー名が**どこに**現れてもマッチします。ネストされたJSON APIでは、これはしばしば広すぎます。特定のキーをどこでも除外せずに、1つの特定のフィールドの値を除外したい場合があるでしょう。その場合は、ドット記法の**パス**でマッチし、`fnmatch` ワイルドカードをサポートする `safe_paths` を使用してください。```php
// config/threat-detection.php
'safe_paths' => ['search.query', 'filters.*.value'],

例えば、search.query{"search": {"query": "..."}} の値(SELECT のような単語を正当に含む検索ボックス)を除外し、リクエスト内の他の場所にある query フィールドは引き続きスキャンされます。リストにないものはすべて、これまでどおりスキャンされます。

ポストマッチバリデータ(チェックサム対応の誤検知削減)

正規表現だけではすべての制約を表現できません。任意の12桁の連続はAadhaarパターンに一致しますが、実際のAadhaar番号はVerhoeffチェックサムも通過します。パターンラベル(デフォルトまたはカスタム)を名前付きバリデータにマッピングすると、正規表現のヒットは、一致した値の少なくとも1つがそれを通過した場合にのみ検出としてカウントされます。```php // config/threat-detection.php 'pattern_validators' => [ 'Aadhaar Number Detected' => 'verhoeff', // shipped default ],

利用可能なバリデータ:

| バリデータ | チェックサム | 一般的な用途 |
|------------|----------|-------------|
| `verhoeff` | Verhoeff | Aadhaar番号 |
| `luhn`     | Luhn     | クレジット/デビットカード番号 |

同梱のマッピングを使用すると、たまたま12桁の長さであるタイムスタンプ、注文ID、バーコードはPIIとしてログに記録されなくなります。一方、本物のAadhaar番号は引き続き記録されます。複数の値が一致し、そのうち1つだけがチェックサムを通過する場合でも、検出は発動します。ノイズの中の本物の番号は依然として漏洩です。

チェックサムで制御されたカード検出のために、独自のパターンとバリデータを組み合わせてください:```php
'custom_patterns'    => ['/\b(?:\d[ -]?){13,19}\b/' => 'Card Number Detected'],
'pattern_validators' => ['Card Number Detected' => 'luhn'],

不明なバリデータ名はフェイルオープンとなる — マッチは未検証のままカウントされ、警告が一度だけログに記録される — そのため、タイポによって検出パターンが静かに無効化されることは決してない。この機能より前に公開された設定には単にそのキーが存在せず、現在の正確な動作が維持される。


編集(検出は保存ではない)

機密データの検出は、かつてそれを保存することを意味していた。携帯番号、PAN、銀行口座を運ぶプロフィールフォームは、3つのPIIパターンに該当し、書き込まれた3つの行のそれぞれがリクエストボディ全体をそのまま保持していた — 完全な保持期間にわたって保持され、ダッシュボードやデータベースへのアクセス権を持つ誰でも読み取れる状態だった。クエリ文字列内の値はurlカラムにも格納された。検出器は、警告対象そのものの、2番目の集中したコピーとなっていた。

v1.7.0以降デフォルトで有効。 ラベルがリストされているパターンが発火すると、マッチした値は保存されたペイロードとURL内でマスクされる:``` BODY: {"name":"Jane Doe","mobile":"[REDACTED]","pan":"[REDACTED]","bank_account":"[REDACTED]"}

アラート、エンドポイント、フィールド名、攻撃元IPはすべて保持されます。削除されるのは値のみです。編集は*検出の後*に実行されるため、見逃しは発生しません。```php
// config/threat-detection.php
'redact' => [
    'enabled' => env('THREAT_DETECTION_REDACT', true),
    'mask'    => '[REDACTED]',
    'labels'  => ['Aadhaar Number Detected', 'PAN Number Detected', /* ... */],
],

攻撃ペイロードは意図的にそのまま残されます。インジェクション文字列は証拠であり秘密ではないため、マスキングすると調査が台無しになります。リストに指定したラベルのみが処理対象となります。

これはセーフフィールドの代替ではありません。セーフフィールドはフィールドがスキャンされるのを防ぎます。一方、リダクションはスキャンを継続しつつ保存を防ぐものです。フォレンジック用に完全なペイロードが必要な場合は、THREAT_DETECTION_REDACT=false を設定してください。


ダッシュボードとAPI認証

ダッシュボードとAPIは、.env を介して設定可能な認証ガードをサポートしています。```env

Options: none (default), auth, role, ip

THREAT_DETECTION_DASHBOARD_GUARD=auth

For role-based guard (Spatie compatible):

THREAT_DETECTION_DASHBOARD_GUARD=role THREAT_DETECTION_DASHBOARD_ROLE=admin

For IP-based guard:

THREAT_DETECTION_DASHBOARD_GUARD=ip THREAT_DETECTION_DASHBOARD_IPS=127.0.0.1,10.0.0.0/8

The same options are available for API routes with `THREAT_DETECTION_API_GUARD`.

When `guard=none` (default), the package logs a warning once per day to remind you to configure authentication.

The guard **fails closed**: an unrecognised guard value (e.g. a typo) is denied with a 403 and a logged warning rather than silently granting access, and `guard=role` denies (with a warning) when the authenticated user model has no `hasRole()` method.

### Disabling a detection needs more than read access

Marking a threat as a false positive and deleting an exclusion rule both silence a detection type for everyone, which is a different privilege from reading the log. Those two endpoints are checked against a separate guard:```env
# Options: none, auth, role, ip. Default: role
THREAT_DETECTION_API_WRITE_GUARD=role

この設定はそれらのルートのみに適用されるため、読み取りとダッシュボードはTHREAT_DETECTION_API_GUARDの指定どおりに動作します。これがない場合、アプリケーションの認証済みユーザーなら誰でも検出機能をオフにできてしまいます。

ユーザーモデルにhasRole()がない場合は、=authを使用してください。1.7.0より前の動作(認証済みユーザーなら誰でも検出を無効化できた)に戻すには、=noneを使用します。この設定中はthreat-detection:doctorが警告を表示します。

ダッシュボード ↔ APIに関する注意: 組み込みダッシュボードは、ブラウザのセッションCookieを使用してAPIルートからデータを取得します。APIルートがauth:sanctumで保護されている場合は、Sanctumのステートフル/SPA認証を設定して(またはダッシュボードをCookie認証ガードに向けて)、それらのAJAX呼び出しが承認されるようにしてください。そうしないと、ダッシュボードは空の状態で表示されます。


カスタムパターン

config/threat-detection.phpで独自の検出用正規表現パターンを追加します:```php 'custom_patterns' => [ '/your-regex-here/i' => 'Your Threat Label', ],

**例 - カスタム管理者エンドポイントのプローブを検出する:**```php
'/\/my-admin-panel/i' => 'Custom Admin Panel Probe',

配列形式(パターンごとのオプション)

従来の文字列形式に加えて、パターンの値は完全な制御のために配列にすることができます:```php 'custom_patterns' => [ '/\b(?:\d[ -]?){13,19}\b/' => [ 'label' => 'Card Number Detected', // required 'level' => 'high', // low|medium|high — overrides keyword derivation 'contexts' => ['query', 'body'], // query|body|headers — default: all segments 'validator' => 'luhn', // post-match checksum, wins over pattern_validators ], ],

- **`level`** は、ラベルの `threat_levels` キーワードから導出する代わりに、脅威レベルを直接設定します。
- **`contexts`** は、スキャンを特定のリクエストセグメントに制限します。例えば、ボディ内でのみ意味を持つカードパターンが、ヘッダー内の数字の連なりに誤って一致するのを防ぎます。
- **`validator`** は、インラインのポストマッチチェックに名前を付けます([ポストマッチバリデータ](#post-match-validators-checksum-aware-false-positive-reduction) を参照)。これは `pattern_validators` ラベルマップよりも優先されます。

文字列と配列のエントリは、同じ設定内で自由に混在できます。不正なオプションは **フェイルオープン** します。つまり、パターンは制限なしでスキャンされ続け、警告がログに記録されるため、設定ミスによって検出が黙って無効化されたり狭められたりすることは決してありません。

> **注:** `/wp-login.php`、`/.env`、`/phpmyadmin` などの一般的なプローブパスは、[404プローブ追跡](#404-probe-tracking) 機能によって自動的に処理されるようになりました。これらに対してカスタムパターンを作成する必要はありません。

各パターンの脅威レベルは、ラベル内のキーワードを `threat_levels` 設定と照合することで自動的に決定されます。```php
'threat_levels' => [
    'high' => ['XSS', 'SQL Injection', 'SQL DDL', 'SQL DML', 'SQL File', 'SQL Hex', 'RCE', ..., 'Shellshock', 'Spring4Shell', 'PowerShell', 'CRLF', 'Null Byte', 'SSTI', 'Java', 'LDAP', 'XPath', 'PHP assert', ...],
    'medium' => ['Directory Traversal', 'LFI', 'SSRF', 'Sensitive', 'Config', ..., 'Open Redirect', 'LF Injection', 'GraphQL', 'Spring Boot Actuator', ...],
    'low' => ['User-Agent', 'JS Redirect', 'SEO Bot', 'Empty', 'Rate', 'Command-line Downloader', 'DNS Rebinding'],
],

ラベルがいずれのキーワードにも一致しない場合、脅威はデフォルトでlow重大度になります。

無効な正規表現パターンは自動的にスキップされ、警告としてログに記録されます。アプリケーションがクラッシュすることはありません。


ファサードの使用

ミドルウェアの外部で脅威データにプログラムからアクセスする場合:```php use JayAnta\ThreatDetection\Facades\ThreatDetection;

// Get attack statistics for a specific IP $stats = ThreatDetection::getIpStatistics('192.168.1.1');

// Detect coordinated attacks (multiple IPs targeting same URL within 15 minutes) $attacks = ThreatDetection::detectCoordinatedAttacks(15, 3);

// Detect attack campaigns (same threat type from 5+ IPs in last 24 hours) $campaigns = ThreatDetection::detectAttackCampaigns(24);

// Get a summary of all correlation data $summary = ThreatDetection::getCorrelationSummary();

// Operator-side decision helpers (see "Acting on the Data") $blocked = ThreatDetection::isBlocklisted('203.0.113.7'); // static denylist, CIDR, whitelist wins $trusted = ThreatDetection::isWhitelisted('10.0.0.5'); $count = ThreatDetection::ddosRequestCount('203.0.113.7'); // requests in the current DDoS window $flooded = ThreatDetection::isDdosThresholdExceeded('203.0.113.7');

---

## 本番環境への移行

このパッケージは設計上パッシブです。リクエストをブロック、拒否、変更することは一切なく、検出ミドルウェアは本体全体を`try/catch`でラップしているため、検出の失敗がアプリを壊すことは決してありません。適切なデフォルト設定が同梱されており、実行に外部サービスは不要です。本番公開前に、この短いチェックリストを確認する価値があります。

1. **ダッシュボードとAPIを保護する。** どちらも初期設定ではゼロコンフィグで初回実行できるよう`guard = none`にデフォルト設定され、保護されていない間は毎日警告をログに記録します。本番環境の前にガードを設定してください - `THREAT_DETECTION_DASHBOARD_GUARD`と`THREAT_DETECTION_API_GUARD`(`auth`、`role`、または`ip`)。認識されない値や、`hasRole()`を持たないユーザーモデルに対する`role`ガードは、現在**フェイルクローズ(403)**となるため、タイプミスでデータが黙って露出することはありません。検出の無効化は、デフォルトで`role`に設定されている`THREAT_DETECTION_API_WRITE_GUARD`によって別途制御されます。[ダッシュボードとAPIの認証](#dashboard-and-api-authentication)を参照してください。
2. **マイグレーションを実行する**(`vendor:publish --tag=threat-detection-migrations && migrate`)。再公開は安全です - すでに公開されたマイグレーションはスキップされます。
3. **検出モードを選択する。** `balanced`(デフォルト)はほとんどのアプリに適しています。コンテンツ中心のサイトでは`relaxed`を、高セキュリティの領域では`strict`を使用します。`content_paths`、`safe_fields`、`min_confidence`で調整します - [誤検知の削減](#reducing-false-positives)を参照してください。
4. **地域別のPII / カスタムパターンを確認する。** デフォルトはインド中心(Aadhaar、PAN、IFSC)であり、広範な数値パターン(例:銀行口座)は、認証ルート外の長い数値IDに一致する可能性があります。お住まいの地域とアプリに合わせて`custom_patterns`を置換または削減し、コンテンツ量の多いルートを`auth_paths` / `content_paths`に追加してください。
5. **ボリュームが見込まれる場合は保持を有効にする:** `THREAT_DETECTION_RETENTION=true`(スケジューラー経由で自動パージ)。Laravelのスケジューラー(`schedule:run`)がcronで駆動されている必要があります。
6. **オプションの追加機能(すべてデフォルトでオフ):** Slackアラート(`THREAT_DETECTION_NOTIFICATIONS`)、ジオエンリッチメント(`php artisan threat-detection:enrich` - 無料のip-api.comへのアウトバウンドコールを行う唯一の機能)、およびキュー書き込み(`THREAT_DETECTION_QUEUE` - すでにキューワーカーを実行している場合のみ有効にしてください。それ以外の場合、書き込みは同期でありRedisは不要です)。

コアの検出とロギングには、Redis、キューワーカー、アウトバウンドのネットワークコールは一切必要ありません。

---

## 誤検知の削減

このパッケージは、誤検知を減らすための複数のツールを提供します。状況に合ったものを使用してください:

### セーフフィールドとセーフパス

フィールドをスキャンから完全に除外します。すべての場所で名前を指定するか(`safe_fields`)、ネストされたJSONのドット記法パスで指定します(`safe_paths`)。最もシンプルで最も大雑把なアプローチです - フィールドはスキップされるため、そのフィールドでは検出は一切実行されません。

詳細と例:[セーフフィールド](#safe-fields-false-positive-reduction)。

### コンテンツパスの抑制

CMSエディター、ブログ投稿フォーム、ユーザーがリッチコンテンツを送信するコメントセクションがある場合、それらのパスは誤検知を引き起こすことがよくあります(例:`<script>`コードサンプルを含むブログ投稿)。それらのパスを追加して、低/中レベルのアラートを抑制します:```php
// config/threat-detection.php
'content_paths' => [
    'admin/posts/*',
    'admin/pages/*',
    'blog/*/edit',
    'comments',
],

これらのパスでは、高重大度の脅威のみがログに記録されます。

誤検知の報告

ダッシュボード内の任意の脅威で FP ボタンをクリックすると、誤検知としてマークできます。これにより:

  1. 脅威が is_false_positive = true としてフラグ付けされます
  2. 同じURL/タイプからの類似脅威が今後抑制されるように、除外ルールが自動的に作成されます

除外ルールはAPI経由で管理します:```bash GET /api/threat-detection/exclusion-rules DELETE /api/threat-detection/exclusion-rules/{id}

### 信頼度スコアリング

すべての脅威には、以下に基づく信頼度スコア(0〜100)が付与されます:
- 同じリクエスト内でのパターンマッチ数
- マッチしたパターンの重大度
- パターンが見つかった場所(クエリ文字列 > ヘッダー > ボディ)
- ユーザーエージェントが既知の攻撃ツールと一致するかどうか
- 現在の検出モード

検出モードの信頼度しきい値を下回る脅威はログに記録されません([検出モード](#detection-modes)を参照)。

---

## 検出される攻撃タイプ

| カテゴリ | 例 |
|----------|---------|
| **SQLインジェクション** | UNION、ブール型、時間ベース、CHARエンコーディング、DDL(DROP/ALTER/CREATE)、DML(INSERT/UPDATE/DELETE)、ファイル操作(INTO OUTFILE、LOAD_FILE)、ORDER BY列挙、16進文字列、UNHEX |
| **NoSQLインジェクション** | MongoDBの$ne、$gt、$regex、$where演算子 |
| **XSS** | スクリプトタグ、SVGイベントハンドラ(`<svg onload=`)、HTMLイベントハンドラ(`<body onload=`、`<img onerror=`)、CSS式、JavaScript URI、DOM操作 |
| **コード実行** | RCEシェル関数、PHPデシリアライゼーション、Javaデシリアライゼーション(base64 + 16進マジックバイト)、テンプレートインジェクション(Blade、JSP、ASP、Jinja2、Velocity)、eval()、base64デコード、PHP assert()、create_function()、preg_replace /e |
| **SSTI** | 数学的プローブ(`{{7*7}}`)、Jinja2 import/config、Velocityテンプレート、Expression Language |
| **コマンドインジェクション** | Linux(シェル関数、コマンドチェーン、curl、wget、nc)、Windows(cmd.exe、PowerShell、wscript、cscript、net user) |
| **ファイルアクセス** | ディレクトリトラバーサル、LFI/RFIプロトコル、機密ファイルのプローブ(.env、.git、composer.json) |
| **SSRF** | ローカルホスト(127.0.0.1、0.0.0.0、::1)、AWS/GCPメタデータ、プライベートIP、16進/10進エンコードされたローカルホスト、DNSリバインディング(xip.io、nip.io、sslip.io) |
| **LDAPインジェクション** | LDAPフィルタ操作、ORインジェクション |
| **XPathインジェクション** | 属性セレクタ、XPath関数(contains、substring) |
| **CRLF / ヘッダーインジェクション** | URLエンコードされたCRLF(`%0d%0a`)、LFインジェクション、ヌルバイトインジェクション |
| **プロトコル攻撃** | HTTPリクエストスマグリング(CL+TE)、SSIインジェクション |
| **CVEエクスプロイト** | Shellshock(CVE-2014-6271)、Spring4Shell(CVE-2022-22965)、PHPUnit RCE(CVE-2017-9841)、Drupalgeddon、Log4Shell |
| **プローブ追跡** | WordPress(`/wp-admin`、`/wp-login.php`)、設定ファイル(`/.env`、`/.git`)、データベースツール(`/phpmyadmin`)、テクノロジープローブ(`.asp`、`.jsp`)、Spring actuator、Swagger/APIドキュメント - 50以上のパス |
| **スキャナー** | SQLMap、Nikto、Nmap、Burp Suite、FeroxBuster、FFUF、XSStrike、Dalfox、Netsparker、Qualys、Nuclei、およびその他20以上(合計53) |
| **AIスクレイパー** | GPTBot、ClaudeBot、ChatGPT、ByteSpider、Cohere、Common Crawl |
| **ヘッドレスブラウザ** | HeadlessChrome、PhantomJS、Selenium、Puppeteer、Playwright |
| **ボット** | Pythonスクリプト、Go HTTPクライアント、cURL、wget、AhrefsBot、SEMRushBot、空のユーザーエージェント |
| **認証** | ブルートフォース検出、トークン漏洩、パスワード露出、セッションID露出 |
| **DDoS** | レートベースの過剰リクエスト検出 |
| **回避技術** | SQLコメント挿入、二重URLエンコーディング、HTMLエンティティエンコーディング、Unicodeエスケープ、IIS Unicode、16進エスケープ |
| **その他** | GraphQLイントロスペクション、プロトタイプ汚染、オープンリダイレクト、XXE、Webシェル、暗号通貨マイニング、PII検出 |

---

## テストスイートの実行```bash
composer test

パッケージには335件のテスト(856件のアサーション)が含まれており、検出パターン、ミドルウェアの動作、APIエンドポイント、信頼度スコアリング、除外ルール、DDoS検出、回避耐性、CVEパターン、LDAP/XPath/SSTIインジェクション、ボット/スキャナー検出、プローブ追跡、エクスポートコマンド、ダッシュボード認証、安全なフィールド、パフォーマンス最適化、およびHTTPからDBへの全サイクル検証をカバーしています。


ライセンス

MITライセンス。詳細はLICENSEを参照してください。

貢献

貢献は大歓迎です!プルリクエストを送信してください。

クレジット

カテゴリ