
laravel-threat-detection v1.7.2
被动式 Laravel 中间件,用于检测并记录 SQL 注入、XSS、RCE、bot 扫描器及 175+ 种攻击模式。内置仪表盘、Slack 告警、REST API 和地理位置增强功能。属于 IDS,而非 WAF。
Laravel 威胁检测
面向 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 是否在一小时内尝试过其他四十种操作。
这个包回答了这些问题。将它放入任何 Laravel 10–13 应用,它就会开始针对 150 多种攻击模式扫描每个 HTTP 请求,按置信度对每次匹配评分,并写入你的数据库——附带内置仪表盘、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)。
## 一分钟内开始使用```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)——完整代码片段见下方 快速开始。
就这样,检测已生效。```bash
php artisan threat-detection:doctor # confirms it is actually recording
---
## 定位:IDS 与 WAF 及边缘服务的区别
本包是一个**被动的、应用层 IDS**——它负责监视和记录,不会拦截。
它旨在与 WAF 或边缘服务*并存*,而不是取代它们。每一层都能看到其他层看不到的东西:
| | **本包**(应用 IDS) | **WAF**(mod_security、Cloudflare WAF) | **边缘/CDN**(Cloudflare) |
|---|:---:|:---:|:---:|
| 拦截恶意请求 | ❌ 仅记录 | ✅ | ✅ |
| 完整的应用上下文(精确路由、解码后的载荷、已认证用户) | ✅ | ⚠️ 部分 | ❌ |
| 内置仪表盘 + 数据库中的威胁日志 | ✅ | ⚠️ 视情况而定 | ⚠️ 仅边缘 |
| 应用特定检测(例如 Aadhaar / PAN / IFSC PII) | ✅ 自定义模式 | ❌ | ❌ |
| 离线工作 / 无需外部服务 | ✅ | ⚠️ 视情况而定 | ❌ |
| 在流量到达应用前阻止 | ❌ | ✅ 边缘 | ✅ |
| 安装 | 一次 `composer require` | 中–高 | 低–中 |
| 成本 | 免费,MIT | 视情况而定 | 免费层 + 付费 |
**简而言之:** 边缘/WAF 是你门上的锁;这是*内部*的监控摄像头,
具备应用上下文,能准确告诉你哪个路由上正在尝试什么、是谁、频率如何。
用它来驱动真正的决策——fail2ban 封禁、速率限制、
地理封锁——使用你的边缘层永远看不到的数据。
### 它刻意不是什么
- **不是 WAF。** 它从不拦截、过滤或修改请求。请使用 Cloudflare、
mod_security 或真正的 WAF 来执行强制措施。(没有边缘层可交接?[操作方辅助工具](#acting-on-the-data-operator-side-blocking)会暴露
本包的决策,以便你编写自己的五行拦截中间件——
强制代码仍归你所有,而非本包。)
- **不是安全编码的替代品。** 参数化查询、输入验证和
输出转义才是你真正的防线。本包假定你的代码已经是
安全的,并为你提供*可见性*,而非保护。
- **不是边缘服务。** 如果你能在前面部署 Cloudflare,那就部署——然后再添加本包以获取
边缘服务无法看到的应用级细节。
### 那么你实际用它做什么?
关于一个从不拦截的检测器,最常见的问题。四个答案,按
投入精力递增排列:
| 你想要 | 使用 | 精力 |
|---|---|---|
| 查看什么在攻击你 | [仪表盘](#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` | 发送内容安全策略(CSP)头 | ✅ 浏览器 | 你想限制浏览器加载的内容 |
| `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 或队列 worker**。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
您应看到一个包含总威胁数、严重性计数和排名靠前的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` 显示零威胁” / “威胁未存储到数据库中”**
如果诊断通过,说明安装没有问题,问题出在测试请求本身。以下三件事它无法替你检查:
| 检查项 | 如何验证 |
|-------|---------------|
| 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 种 Bot/扫描器特征 - SQLMap、Nikto、Nmap、Burp Suite、FeroxBuster、FFUF、XSStrike、Dalfox、Netsparker 以及其他 70 多种扫描器和 Bot 特征
- AI 爬虫检测 - GPTBot、ClaudeBot、ByteSpider、Common Crawl 及其他 AI 训练 Bot
- 无头浏览器检测 - HeadlessChrome、PhantomJS、Selenium、Puppeteer、Playwright
- 404 探测追踪 - 检测针对已知易受攻击路径(
/wp-admin、/.env、/phpmyadmin、/actuator等)的侦察探测,内置 50+ 默认探测路径 - DDoS 监控 - 基于速率阈值的检测,支持可配置时间窗口
- 置信度评分 - 每个威胁根据模式数量、上下文和信号获得 0-100 的置信度评分
- 抗绕过能力 - 归一化处理流程可在模式匹配前消除 SQL 注释插入、双重 URL 编码、HTML 实体编码、Unicode 转义和十六进制转义
- 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 CDN,零构建步骤)
- 仪表盘认证守卫 - 仪表盘和 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).
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',
],
留空(默认)以扫描所有路由(受 `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 次重试,退避 10 秒/30 秒)。检测仍然实时进行——只有写入被延迟。
自动清理(保留策略)
按每日计划自动删除旧的威胁日志:```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,
],
];
该事件携带 $threatLog(完整的数据库行数组)、$ipAddress 和 $threatLevel。使用它来触发自定义操作——发送 Telegram 警报、更新阻止列表、馈送 SIEM 等。
DdosThresholdExceeded 事件
当客户端超过配置的 DDoS 阈值(在 ddos.window 秒内达到 ddos.threshold 次请求)时,会随威胁日志条目一起触发一个 DdosThresholdExceeded 事件:```php
use JayAnta\ThreatDetection\Events\DdosThresholdExceeded;
protected $listen = [ DdosThresholdExceeded::class => [ YourFloodListener::class, ], ];
该事件携带 `$ipAddress`、`$requestCount`、`$threshold` 和 `$windowSeconds`。它按每个 IP 在每个去重窗口内被节流(与日志行的节流相同),因此洪水攻击无法淹没你的监听器。你可以用它来发出警报或馈送到外部封禁存储;若要*拒绝*超过阈值的客户端,请改为在你自己的中间件中使用 `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 频道已被移除。该包会自动检测此情况,并向你的 Slack URL 发送原始 HTTP POST webhook。无需额外安装包。如果你更倾向于使用完整的通知频道,请安装:```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,你有两个选项:
选项 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` | 最近一小时的威胁数量 |
| 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` | 每页条目数(默认: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,请包含认证头,或为基于 Cookie 的请求配置 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。如果未配置,两件事会同时出错:每个请求看起来都来自代理,因此黑名单条目会阻止你的全部流量或完全不阻止——更糟糕的是,如果应用信任了本不该信任的转发头,攻击者可以设置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,这通常过于宽泛——你可能只想豁免某个特定字段的值,而不想在所有位置豁免该键。请使用 `safe_paths`,它通过点号表示法的**路径**进行匹配,并支持 `fnmatch` 通配符:```php
// config/threat-detection.php
'safe_paths' => ['search.query', 'filters.*.value'],
例如,search.query 会豁免 {"search": {"query": "..."}} 的值(一个搜索框,其文本合法地包含诸如 SELECT 之类的词),而请求中其他任何位置的 query 字段仍会被扫描。未列出的所有内容均照常扫描。
匹配后验证器(基于校验和的误报减少)
仅靠正则表达式无法表达所有约束:任何 12 位数字串都匹配 Aadhaar 模式,但真实的 Aadhaar 号码还会通过 Verhoeff 校验和。将模式标签(默认或自定义)映射到命名验证器后,只有当至少一个匹配值通过该验证器时,正则命中才算作检测:```php // config/threat-detection.php 'pattern_validators' => [ 'Aadhaar Number Detected' => 'verhoeff', // shipped default ],
可用验证器:
| 验证器 | 校验和 | 典型用途 |
|------------|----------|-------------------|
| `verhoeff` | Verhoeff | Aadhaar 号码 |
| `luhn` | Luhn | 信用卡/借记卡号码 |
借助内置映射,恰好为 12 位数字的时间戳、订单 ID 和条形码不再被记录为 PII——而真正的 Aadhaar 号码仍会被识别。如果多个值匹配且只有一个通过校验和,检测仍会触发:噪声中的真实号码依然是泄露。
将验证器与您自己的模式配对,即可实现基于校验和的银行卡检测:```php
'custom_patterns' => ['/\b(?:\d[ -]?){13,19}\b/' => 'Card Number Detected'],
'pattern_validators' => ['Card Number Detected' => 'luhn'],
未知的验证器名称默认放行——该匹配项在未经验证的情况下被计入,并仅记录一次警告日志——因此拼写错误绝不会静默禁用某个检测模式。在此功能之前发布的配置根本没有该键,因此会保持其现有的确切行为。
脱敏(检测不等于存储)
检测敏感数据过去意味着存储它。一个携带手机号码、PAN 和银行账户的个人资料表单会触发三条 PII 模式,而写入的三行记录中每一行都逐字保留了完整的请求体——在整个保留期内持续留存,任何拥有仪表板或数据库访问权限的人都能读取。查询字符串中的值也会落入 url 列。检测器变成了它所警告内容的第二份、高度集中的副本。
自 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
API 路由同样支持这些选项,可通过 `THREAT_DETECTION_API_GUARD` 配置。
当 `guard=none`(默认值)时,该包每天记录一次警告,提醒你配置身份验证。
该守卫采用**失败即拒绝**策略:无法识别的守卫值(例如拼写错误)会被拒绝并返回 403,同时记录警告,而不是静默授予访问权限;当已认证的用户模型没有 `hasRole()` 方法时,`guard=role` 也会拒绝访问(并记录警告)。
### 禁用某项检测需要的不只是读取权限
将威胁标记为误报以及删除排除规则,都会对所有人静默某种检测类型,这与读取日志的权限不同。这两个端点会通过单独的守卫进行检查:```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。
无效的正则表达式模式会被自动跳过并记录为警告——它们不会导致你的应用程序崩溃。
使用 Facade
如需在中间件之外以编程方式访问威胁数据:```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)**,因此拼写错误不会静默暴露数据。禁用检测由 `THREAT_DETECTION_API_WRITE_GUARD` 单独控制,其默认值为 `role`。参见 [仪表盘和 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 按钮,将其标记为误报。此操作会:
- 将该威胁标记为
is_false_positive = true - 自动创建排除规则,以便今后抑制来自同一 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枚举、十六进制字符串、UNHEX |
| **NoSQL注入** | MongoDB $ne、$gt、$regex、$where 运算符 |
| **XSS** | 脚本标签、SVG事件处理器(`<svg onload=`)、HTML事件处理器(`<body onload=`、`<img onerror=`)、CSS表达式、JavaScript URI、DOM操作 |
| **代码执行** | RCE shell函数、PHP反序列化、Java反序列化(base64 + 十六进制魔数)、模板注入(Blade、JSP、ASP、Jinja2、Velocity)、eval()、base64解码、PHP assert()、create_function()、preg_replace /e |
| **SSTI** | 数学探测(`{{7*7}}`)、Jinja2 import/config、Velocity模板、表达式语言 |
| **命令注入** | Linux(shell函数、命令链、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、十六进制/十进制编码的本地主机、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、十六进制转义 |
| **其他** | GraphQL内省、原型污染、开放重定向、XXE、Web Shell、加密货币挖矿、PII检测 |
---
## 运行测试套件```bash
composer test
该软件包包含335项测试(856个断言),涵盖检测模式、中间件行为、API端点、置信度评分、排除规则、DDoS检测、规避抵抗、CVE模式、LDAP/XPath/SSTI注入、机器人/扫描器检测、探测跟踪、导出命令、仪表板认证、安全字段、性能优化以及HTTP到数据库的全周期验证。
许可证
MIT许可证。详情请参阅LICENSE。
贡献
欢迎贡献!请提交拉取请求。
致谢
- Jay Anta - 作者与维护者
- David van der Tuijn - Laravel 13支持
- 所有贡献者