被动式 Laravel 中间件,用于检测并记录 SQL 注入、XSS、RCE、bot 扫描器及 175+ 种攻击模式。内置仪表盘、Slack 告警、REST API 和地理位置增强功能。属于 IDS,而非 WAF。
<p align="center">
<a href="https://packagist.org/packages/jayanta/laravel-threat-detection"><img src="https://img.shields.io/packagist/v/jayanta/laravel-threat-detection.svg?style=flat-square" alt="Latest Version"></a>
<a href="https://github.com/jay123anta/laravel-threat-detection/actions/workflows/tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/jay123anta/laravel-threat-detection/tests.yml?branch=main&style=flat-square&label=tests" alt="Tests"></a>
<a href="https://github.com/jay123anta/laravel-threat-detection/actions/workflows/tests.yml"><img src="https://img.shields.io/badge/PHPStan-level%205-brightgreen?style=flat-square" alt="PHPStan Level 5"></a>
<a href="https://github.com/jay123anta/laravel-threat-detection/actions/workflows/tests.yml"><img src="https://img.shields.io/badge/code%20style-Pint-orange?style=flat-square" alt="Code Style Pint"></a>
<a href="https://packagist.org/packages/jayanta/laravel-threat-detection"><img src="https://img.shields.io/packagist/dt/jayanta/laravel-threat-detection.svg?style=flat-square" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/jayanta/laravel-threat-detection"><img src="https://img.shields.io/packagist/php-v/jayanta/laravel-threat-detection?style=flat-square" alt="PHP Version"></a>
<a href="https://github.com/jay123anta/laravel-threat-detection/blob/main/LICENSE"><img src="https://img.shields.io/packagist/l/jayanta/laravel-threat-detection.svg?style=flat-square" alt="License"></a>
</p>
# Laravel Threat Detection
**面向 Laravel 的安全监控与攻击日志记录。检测并记录 SQL 注入、
XSS、RCE、目录遍历、机器人扫描器以及 `/wp-admin` 风格的侦察探测——
每个恶意请求都会连同完整的应用上下文记录到你的数据库中。
它是 IDS,不是 WAF:它从不拦截、过滤或修改请求。**
<p align="center">
<img src="https://raw.githubusercontent.com/jay123anta/laravel-threat-detection/main/art/demo.gif" alt="安装该包,发送三次攻击——SQL 注入、目录遍历、XSS——每一次都返回 HTTP 200,因为没有任何请求被拦截,而三次攻击都已计入 threat-detection:stats" width="100%">
</p>
### 你是因为看到类似这样的内容才来到这里的吗?
```
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 是否在这一小时内尝试了其他四十种操作。
这个包回答了这些问题。把它放入任何 Laravel 10–13 应用,它就会开始
针对 150 多种攻击模式扫描每一个 HTTP 请求,按置信度对每个匹配项评分,
并将其写入你的数据库——内置仪表盘、Slack 告警、地理位置信息增强,
以及 fail2ban/阻止列表导出。任何请求都不会被阻止。把它想象成监控摄像头,
而不是锁:它准确地向你展示谁在探测你的路由、频率如何,以及使用了什么技术。
> 从生产应用中提取,并在真实流量中经过实战检验。2,400+ 项测试,除 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)。
## 不到一分钟即可开始
```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` 中添加一行)——完整代码片段见下方的[快速开始](#quick-start)。就这样,检测已生效。
```bash
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、解码后的载荷、已认证用户) | ✅ | ⚠️ 部分 | ❌ |
| 内置仪表盘 + 数据库中的威胁日志 | ✅ | ⚠️ 因情况而异 | ⚠️ 仅边缘 |
| 应用特定的检测(例如 Aadhaar / PAN / IFSC 个人身份信息) | ✅ 内置 + 自定义 | ⚠️ 需自行编写规则 | ⚠️ 自定义规则,取决于套餐 |
| 可离线运行 / 无需外部服务 | ✅ | ⚠️ 视情况而定 | ❌ |
| 在流量到达服务器之前将其拦截 | ❌ | ⚠️ 仅云 WAF | ✅ |
| 安装配置 | `composer require`、迁移、一行中间件 | 中–高 | 低–中 |
| 成本 | 免费,MIT | 因情况而异 | 免费套餐 + 付费 |
**简而言之:** 边缘/WAF 是你门上的锁;而这是*内部*的安全摄像头,凭借应用上下文,它能准确告诉你:谁在哪个路由上尝试了什么、尝试了多少次。用它来为真实决策提供依据——fail2ban 封禁、速率限制、地理封锁——这些数据是你的边缘层永远看不到的。
### 它刻意不做的事
- **不是 WAF。** 它从不拦截、过滤或修改请求。请使用 Cloudflare、mod_security 或真正的 WAF 来执行拦截。(没有可交接的边缘层?[运维侧辅助工具](#acting-on-the-data-operator-side-blocking)会暴露本包的判定结果,让你可以编写自己的五行拦截中间件——执行代码归你所有,而非本包。)
- **不能替代安全编码。** 参数化查询、输入验证和输出转义才是你真正的防线。本包假设你的代码已经安全,并为你提供*可见性*,而非保护。
- **不是边缘服务。** 如果你能在前面部署 Cloudflare,那就部署——然后再加上本包,以获取边缘服务无法看到的应用层细节。
- **不是完整的检测器,也不可能做到。** 模式匹配只能捕获*看起来像*已知攻击的攻击。一种新颖的技术,或一种被改写得足够多的熟悉技术,都会在未被记录的情况下通过——而且你也不会被告知它通过了。这里的沉默意味着“没有匹配到”,而绝不是“什么都没发生”。它的价值在于应对那些构成实际攻击公开 Laravel 应用的大部分流量:扫描器、侦察探测、现成的注入字符串、凭据喷洒。把安静的日志视为缺乏证据,而非没有发生。
### 预期它第一天就会标记你自己的内容
未经调优的安装会误报合法内容,你应当在安装之前而非之后了解这一点。以下数据是实测的,而非假设。下表是 [`LegitimateTrafficCorpusTest`](https://github.com/jay123anta/laravel-threat-detection/blob/main/tests/Feature/LegitimateTrafficCorpusTest.php) 所固定噪声下限的样本;每一行都列出了该请求所记录的*全部*内容,而 [`ReadmeNoiseFloorTest`](https://github.com/jay123anta/laravel-threat-detection/blob/main/tests/Feature/ReadmeNoiseFloorTest.php) 会在任何一行不再匹配测试套件所测量的结果时使构建失败。
<!-- noise-floor:start -->
| 完全合法的请求 | 未经调优的安装会记录什么 |
|---|---|
| 在搜索框中输入 `how to write a UNION SELECT in postgres` <!-- case: sql tutorial in a search box --> | `SQL Injection UNION` / 高 |
| 一篇包含 `<script>window.dataLayer=[];</script>` 的博客文章 <!-- case: javascript snippet in a blog post --> | `XSS Script Tag` / 高 · `Command Chain Injection` / 中 |
| 一张粘贴了 `SELECT * FROM users WHERE id = 1` 错误的支持工单 <!-- case: a pasted sql error in a support ticket --> | `SQLi Variant` / 高 · `SQL SELECT Query` / 低 |
| 解释 `../../etc/passwd` 是经典遍历载荷的文档 <!-- case: a traversal explainer in documentation --> | `Directory Traversal` / 中 · `Sensitive File Access` / 中 |
| 一个收集真实印度手机号和 PAN 的个人资料表单 <!-- case: a profile form collecting genuine indian pii --> | `PAN Number Detected` / 高 · `Bank Account Number Detected` / 高 · `Mobile Number Detected` / 低 |
<!-- noise-floor:end -->
**这些都不是缺陷。** 一篇包含 `<script>` 的博客文章,逐字节来看就是一个存储型 XSS 载荷;搜索 `UNION SELECT` 与尝试注入无法区分;一个十位手机号也可能是一个合理的银行账号。除了应用上下文,没有任何东西能将它们区分开来,而没有任何模式引擎能为你提供这种上下文。
提供上下文只需一行配置更改:在 `safe_fields` 中指定合法携带此类内容的字段(或使用 `safe_paths`,针对嵌套 JSON 中的某个路径),它就不再被扫描。这就是让上表各行安静下来的开关——而这是一个真实的权衡,因为通过该字段传递的攻击也会被忽略。
`content_paths` 和 `relaxed` 模式是不同的工具。它们会丢弃低和中危噪声,并**刻意继续记录高危匹配**——因此按设计它们不会让本表中的高危行安静下来。关于何时该用哪个,请参阅[减少误报](#reducing-false-positives)。
**如果你的应用接受富文本、代码示例或搜索查询,请在评判输出之前先做这件事。** 默认设置刻意选择嘈杂但诚实,而非安静但不完整:让一个已知匹配安静下来,比发现一个从未触发的匹配要容易。
### 那么你实际上能用它做什么?
关于一个从不拦截的检测器,最常见的问题。四个答案,按投入递增排列:
| 你想要 | 使用 | 投入 |
|---|---|---|
| 查看什么在攻击你 | [仪表盘](#dashboard) 或 `threat-detection:stats` | 无,它已经在运行 |
| 在防火墙上封禁惯犯 | [`threat-detection:export-fail2ban`](#artisan-commands) — 管道到 cron | 一行 |
| 在 Web 服务器层面拒绝 | [`threat-detection:export-blocklist`](#artisan-commands) → nginx/apache 指令 | 一行 |
| 在应用内拒绝请求 | [运维侧辅助工具](#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. 请求会与 158 种正则模式进行匹配,涵盖 SQL 注入、XSS、RCE、文件遍历、SSRF、LDAP、XPath、SSTI 等
3. 如果匹配到威胁模式,一条记录会写入你的 `threat_logs` 数据库表,包含 IP、URL、威胁类型、严重级别和置信度评分
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`(存储误报规则)。
**验证表是否已创建:**
```bash
php artisan migrate:status
```
查找 `create_threat_logs_table`、`add_confidence_to_threat_logs_table` 和 `create_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
```
你应该会看到一个包含 `Recorded Detections`、严重性计数和顶级 IP 的表格。该数字统计的是*行数*,而非尝试次数:每个 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 分钟仅记录一次 | 去重:相同 IP + 相同威胁类型会被缓存 5 分钟。每次测试请使用**不同的攻击类型**,或在测试之间等待。 |
| `curl` 请求会触发额外检测 | 使用 `curl` 还会记录一条 "cURL Command" 用户代理检测(低严重性)。这是预期行为——该包会检测自动化工具。 |
| 该包从不阻止请求 | 你的应用会继续正常运行。检测是被动的。 |
| 无需设置 Slack | 通知默认关闭。 |
| 无需互联网连接 | 核心检测 100% 本地运行。只有可选的 `threat-detection:enrich` 命令会调用外部 API 获取地理数据。 |
### 故障排除
**从这里开始——一条命令即可解答大部分问题:**
```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.
```
它涵盖的内容:为此环境启用的检测;写入器所需的每一列(缺少任何一列都会丢弃**所有**威胁);仪表板/API 列;排除规则表;中间件是否实际连接到路由或路由组;早于当前版本的已发布配置;遮蔽内置模式的自定义模式;无法进行 DDoS 计数的缓存驱动;以及未启用身份验证而对外开放的仪表板或 API。
**“我测试了,但 `threat-detection:stats` 显示零威胁” / “威胁未存储到数据库中”**
如果 doctor 检查通过,说明安装没问题,问题出在测试请求本身。有三件事它无法替你检查:
| 检查项 | 验证方法 |
|-------|---------------|
| IP 未被列入白名单 | 如果你在 `.env` 中添加了 `THREAT_DETECTION_WHITELISTED_IPS`,测试期间请将其移除 |
| 使用了已存在的路由 | 测试 URL 必须匹配真实路由(例如 `/`)。返回 404 意味着中间件从未运行 |
| 去重缓存 | 相同 IP + 相同攻击类型会被缓存 5 分钟——请尝试不同的攻击类型 |
> 仅运行 `php artisan migrate` 永远不够:迁移文件位于包内部,必须先发布到应用的 `database/migrations/` 目录。当问题出在这里时,doctor 会打印出确切的命令。
**“API 返回 401 Unauthorized”**
请参阅下方的 [API 身份验证](#api-authentication)。
**“仪表板显示 404”**
仪表板默认处于禁用状态。将 `THREAT_DETECTION_DASHBOARD=true` 添加到 `.env` 并清除路由缓存:
```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+ 默认探测路径,另提供可选的 [AI 基础设施包](#ai-infrastructure-probes-opt-in) 用于暴露的 LLM 网关
- **DDoS 监控** - 基于速率的阈值检测,窗口可配置
- **置信度评分** - 每个威胁根据模式数量、上下文和信号获得 0-100 的置信度评分
- **抗规避** - 归一化管道在模式匹配前可破解 SQL 注释插入、双重 URL 编码、HTML 实体编码、Unicode 转义、十六进制转义和 IIS `%uXXXX` 编码——重复执行直到载荷不再变化,因此叠加两种技术也无法绕过
- **CVE 检测** - Shellshock(CVE-2014-6271)、Spring4Shell(CVE-2022-22965)、PHPUnit RCE(CVE-2017-9841)、Drupalgeddon、Log4Shell
- **上下文感知检测** - 在查询字符串中发现的模式比请求体中的评分更高
- **请求体扫描** - 同时检查表单编码和 JSON(`application/json`)请求体
- **安全字段** - 从扫描中排除特定表单字段(用于 CMS 编辑器、代码输入、搜索字段)
- **误报报告** - 从仪表板将威胁标记为误报;自动创建排除规则
- **三种检测模式** - `strict`、`balanced`(默认)和 `relaxed`——可调灵敏度
- **内容路径抑制** - 将 CMS/博客路径加入白名单,以抑制来自富文本内容的低/中等级别警报
- **PII 检测** - 敏感数据暴露模式(可按地区配置)
- **地理位置增强** - 通过免费 API 识别国家、城市、ISP、云服务提供商
- **Slack 警报** - 针对高严重性威胁的实时通知(适用于 Laravel 10 和 11+)
- **内置仪表板** - 深色模式 Blade 仪表板(Alpine.js 和 Tailwind,样式已预编译,零构建步骤)
- **仪表板认证守卫** - 可配置的仪表板和 API 认证(无、auth、角色或基于 IP)
- **15 个 API 端点** - 完整的 REST API,用于构建自定义 Vue/React/移动端仪表板
- **Fail2ban 导出** - 以 fail2ban 兼容格式或纯黑名单导出检测到的 IP
- **黑名单导出** - 以 nginx deny、Apache deny、CSV 或纯文本格式导出 IP
- **CSV 导出** - 一键导出威胁日志(最多 10,000 行)
- **关联分析** - 检测跨 IP 的协同攻击和攻击活动
- **性能优化** - 基于类别的惰性模式加载(仅对相关攻击类别运行正则表达式)、干净请求的提前退出、浏览器 UA 短路(对普通浏览器跳过 70+ 项检查)、探测路径哈希查找、批量数据库插入、可配置的每请求最大检测数
- **数据库无关** - 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).
# HTTPS since v1.8.0. A failed lookup is never retried over cleartext, and a
# run in which every lookup failed exits non-zero rather than reporting
# success. Enrichment is opt-in either way.
#
# ip-api.com's free tier rejects HTTPS, so on the free tier this command will
# now fail rather than quietly sending your visitors' IP addresses in the
# clear. Either point it at a provider you hold a key for, or set it back
# explicitly and accept the disclosure.
# THREAT_DETECTION_GEO_ENDPOINT=https://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` | 25 | 仅运行高严重性模式。最适合误报频繁的内容密集型站点。(在 v1.8.0 之前为 40——这是任何单一检测都无法达到的下限,因此该模式会静默地不记录任何内容。) |
### 启用的环境
默认情况下,检测在 `production`、`staging` 和 `local` 中运行。要更改,请发布配置并编辑:
```php
'enabled_environments' => ['production', 'staging', 'local'],
```
要在测试套件中禁用检测,请设置 `APP_ENV=testing`(不在上述列表中)或添加到你的 `phpunit.xml` 中:
```xml
<env name="THREAT_DETECTION_ENABLED" value="false"/>
```
### 配置参考
发布配置文件以查看所有可用选项:
```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',
],
```
留空(默认)以扫描所有路由(受 `skip_paths` 约束)。当两者都配置时,先检查 `only_paths`,然后在匹配的集合内应用 `skip_paths`。
### 队列支持
默认情况下,威胁日志记录在请求周期内同步进行。对于高流量应用,你可以将数据库写入和 Slack 通知卸载到队列:
```env
THREAT_DETECTION_QUEUE=true
THREAT_DETECTION_QUEUE_CONNECTION=redis
THREAT_DETECTION_QUEUE_NAME=threat-logs
```
这会派发一个 `StoreThreatLog` 任务(3 次重试,退避 10s/30s)。检测仍然实时进行——只是写入被延迟。如果队列无法接收该任务,则改为直接写入这些行,并在日志中记录一条警告,因此队列故障不会丢失检测结果。
### 自动清除(保留策略)
按每日计划自动删除旧的威胁日志:
```env
THREAT_DETECTION_RETENTION=true
THREAT_DETECTION_RETENTION_DAYS=90
```
需要 Laravel 的调度器正在运行(`php artisan schedule:run`)。每天 02:00 通过 `threat-detection:purge` 运行。
`THREAT_DETECTION_RETENTION_DAYS` 必须是大于等于 1 的整数;其他任何值
都不会安排清除,并且 `threat-detection:doctor` 会报告该情况。
### ThreatDetected 事件
每个确认的威胁都会派发一个 `ThreatDetected` 事件,你可以监听它:
```php
// app/Providers/EventServiceProvider.php
use JayAnta\ThreatDetection\Events\ThreatDetected;
protected $listen = [
ThreatDetected::class => [
YourCustomListener::class,
],
];
```
该事件携带 `$threatLog`(完整数据库行数组)、`$ipAddress` 和 `$threatLevel`。使用它来触发自定义操作——发送 Telegram 警报、更新阻止列表、馈送到 SIEM 等。
### DdosThresholdExceeded 事件
当客户端超过配置的 DDoS 阈值(在 `ddos.window` 秒内达到 `ddos.threshold` 次请求)时,会随威胁日志条目一起派发一个 `DdosThresholdExceeded` 事件:
```php
use JayAnta\ThreatDetection\Events\DdosThresholdExceeded;
protected $listen = [
DdosThresholdExceeded::class => [
YourFloodListener::class,
],
];
```
---
[Read more](https://github.com/jay123anta/laravel-threat-detection)