# Laravel 威胁检测
**无需修改任何应用代码,即可了解谁在攻击你的 Laravel 应用。**
这是一个基于中间件的 Laravel 威胁检测与日志记录系统。只需安装,它就会开始扫描每一个 HTTP 请求,检测 SQL 注入、XSS、RCE、扫描器机器人、DDoS 模式等 40 多种攻击类型 —— 将所有信息记录到数据库中,并提供完整的地理信息增强和内置仪表盘。
**重要提示:** 本包**从不拦截**任何请求。它只进行**日志记录**和**报警**。即使检测到威胁,你的应用程序也会继续正常处理每个请求。
## 系统要求
- PHP 8.1+
- Laravel 10.x, 11.x, 或 12.x
- Laravel 支持的任何数据库 (MySQL, PostgreSQL, SQLite, SQL Server)
## 工作原理
1. 中间件扫描每一个传入的 HTTP 请求
2. 请求会通过 130 多个正则表达式进行检测,涵盖 SQL 注入、XSS、RCE、文件遍历、SSRF 等
3. 如果匹配到威胁模式,会在 `threat_logs` 数据库表中写入一条记录,包含 IP、URL、威胁类型、严重程度和置信度分数
4. 可选:针对高严重性威胁发送 Slack 报警
5. 请求继续正常执行 —— **没有任何东西被拦截**
检测过程不需要互联网连接。所有操作都在你的应用内部本地运行。
## 快速开始
### 1. 安装包
```
composer require jayanta/laravel-threat-detection
```
### 2. 发布迁移文件并运行
```
php artisan vendor:publish --tag=threat-detection-migrations
php artisan migrate
```
这会创建两个表:`threat_logs`(存储检测到的威胁)和 `threat_exclusion_rules`(存储误报规则)。
### 3. 注册中间件
中间件负责扫描请求。你需要将其添加到 `web` 中间件组中。
**如果你使用 Laravel 11 或 12** —— 打开 `bootstrap/app.php`:
```
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class,
]);
})
```
**如果你使用 Laravel 10** —— 打开 `app/Http/Kernel.php`:
```
protected $middlewareGroups = [
'web' => [
// ... existing middleware
\JayAnta\ThreatDetection\Http\Middleware\ThreatDetectionMiddleware::class,
],
];
```
### 4. (可选) 发布配置文件
```
php artisan vendor:publish --tag=threat-detection-config
```
该包使用合理的默认值。发布配置允许你自定义检测模式、灵敏度模式、Slack 通知等。如果跳过此步骤,一切仍将正常工作。
**就是这样。** 你的应用现在已经开始检测威胁了。
## 验证是否生效
安装完成后,触发一个测试威胁并确认其已被记录。
### 第一步:启动你的应用
```
php artisan serve
```
### 第二步:在浏览器中打开测试 URL
在你应用的**任何现有路由**(如主页、产品页面等)后追加恶意查询参数。例如:
**SQL 注入:**
```
http://localhost:8000/?q=' UNION SELECT * FROM users--
```
**XSS (跨站脚本攻击):**
```
http://localhost:8000/?q=
```
**目录遍历:**
```
http://localhost:8000/?file=../../etc/passwd
```
**RCE (远程代码执行):**
```
http://localhost:8000/?cmd=system('ls -la')
```
### 第三步:检查威胁是否被记录
**方式 A — Artisan 命令 (最快):**
```
php artisan threat-detection:stats
```
你应该会看到一个包含 `Total Threats`、严重程度计数和顶级 IP 的表格。
**方式 B — Tinker:**
```
php artisan tinker
```
```
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" user-agent 检测(低严重性)。这是预期行为 —— 该包会检测自动化工具。 |
| 该包从不拦截请求 | 你的应用继续正常运行。检测是被动的。 |
| 不需要设置 Slack | 默认情况下通知是关闭的。 |
| 不需要互联网连接 | 核心检测是 100% 本地的。只有可选的 `threat-detection:enrich` 命令会调用外部 API 获取地理数据。 |
### 故障排除
**“我测试了,但 `threat-detection:stats` 显示威胁数为零”**
| 检查项 | 如何验证 |
|-------|---------------|
| 迁移是否已运行 | 运行 `php artisan migrate:status` —— 查找 `threat_logs` 和 `threat_exclusion_rules` 表 |
| 中间件是否已注册 | 确认 `ThreatDetectionMiddleware` 在你的 `web` 中间件组中 (参见上面的 [第三步](#3-register-the-middleware)) |
| IP 是否在白名单中 | 如果你在 `.env` 中添加了 `THREAT_DETECTION_WHITELISTED_IPS`,请在测试期间将其移除 |
| 环境是否已启用 | 默认启用的环境:`production`, `staging`, `local`。检查 `.env` 中的 `APP_ENV` |
| 是否使用了现有路由 | 测试 URL 必须匹配真实路由 (例如 `/`)。 |
| 去重缓存 | 相同 IP + 相同攻击类型会被缓存 5 分钟 —— 尝试使用不同的攻击类型 |
**“`threat-detection:stats` 抛出数据库错误”**
`threat_logs` 表尚不存在。运行:
```
php artisan vendor:publish --tag=threat-detection-migrations
php artisan migrate
```
**“API 返回 401 Unauthorized”**
请参阅下面的 [API 认证](#api-authentication)。
**“仪表盘显示 404”**
仪表盘默认禁用。将 `THREAT_DETECTION_DASHBOARD=true` 添加到 `.env` 并清除路由缓存:
```
php artisan route:clear
```
## 功能特性
- **130+ 检测模式** — SQL 注入、XSS、RCE、目录遍历、SSRF、XXE、Log4Shell、NoSQL 注入、命令注入等
- **扫描器检测** — SQLMap, Nikto, Nmap, Burp Suite, Acunetix, WPScan, Nessus, Nuclei, Metasploit 等
- **机器人检测** — 可疑 user-agent、自动化脚本、无头浏览器
- **DDoS 监控** — 基于速率的阈值检测,具有可配置的时间窗口
- **置信度评分** — 每个威胁都会根据模式数量、上下文和信号获得 0-100 的置信度分数
- **规避抵抗** — Payload 标准化可击败 SQL 注释插入 (`UNION/**/SELECT`)、双重 URL 编码 (`%2527`) 和 CHAR 编码绕过
- **上下文感知检测** — 在查询字符串中发现的模式得分高于 POST body 中的
- **误报报告** — 从仪表盘将威胁标记为误报;自动创建排除规则
- **三种检测模式** — `strict`、`balanced` (默认) 和 `relaxed` — 灵敏度可调
- **内容路径抑制** — 将 CMS/博客路径列入白名单,以抑制来自富文本内容的低/中严重性警报
- **PII 检测** — 敏感数据泄露模式(可按区域配置)
- **地理信息增强** — 通过免费 API 识别国家、城市、ISP、云服务商
- **Slack 报警** — 高严重性威胁的实时通知(适用于 Laravel 10 和 11+)
- **内置仪表盘** — 暗色模式 Blade 仪表盘 (Alpine.js + Tailwind CDN,零构建步骤)
- **15 个 API 端点** — 完整的 REST API,用于构建自定义 Vue/React/移动端仪表盘
- **CSV 导出** — 一键威胁日志导出(最多 10,000 行)
- **关联分析** — 检测跨 IP 的协同攻击和攻击活动
- **数据库无关** — MySQL, PostgreSQL, SQLite, SQL Server
- **零配置** — 开箱即用,具有合理的默认设置
- **设计安全** — 中间件会捕获自身的错误。如果检测失败,你的应用仍会继续运行。请求永远不会被拦截。
## 配置
该包无需更改 `.env` 即可工作。以下所有值都是可选的 —— 仅在你要覆盖默认值时添加它们。
```
# 全局启用/禁用检测 (默认值: true)
THREAT_DETECTION_ENABLED=true
# 检测灵敏度 (默认值: balanced)
# 选项: strict, balanced, relaxed
THREAT_DETECTION_MODE=balanced
# 自定义表名 (默认值: threat_logs)
# THREAT_DETECTION_TABLE=threat_logs
# 白名单 IP 以完全跳过检测 (默认值: 空)
# 支持 CIDR 表示法。以逗号分隔。
# THREAT_DETECTION_WHITELISTED_IPS=10.0.0.0/8,192.168.1.0/24
# DDoS 检测阈值 (显示默认值)
# THREAT_DETECTION_DDOS_THRESHOLD=300
# THREAT_DETECTION_DDOS_WINDOW=60
# Slack 通知 (默认禁用)
# THREAT_DETECTION_NOTIFICATIONS=true
# THREAT_DETECTION_SLACK_WEBHOOK=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
# THREAT_DETECTION_SLACK_CHANNEL=#threat-alerts
# 仪表盘 (默认禁用)
# THREAT_DETECTION_DASHBOARD=true
# API 端点 (默认启用)
# THREAT_DETECTION_API=true
```
### 检测模式
| 模式 | 置信度阈值 | 行为 |
|------|---------------------|----------|
| `strict` | 0 (记录所有内容) | 所有模式激活,最低阈值。能捕获所有内容,但可能会误报合法流量。 |
| `balanced` | 10 | 默认。置信度评分激活,标准阈值。适用于大多数应用。 |
| `relaxed` | 40 | 仅高严重性模式会触发。最适合具有频繁误报的内容密集型网站。 |
### 启用的环境
默认情况下,检测在 `production`、`staging` 和 `local` 中运行。要更改此设置,请发布配置并编辑:
```
'enabled_environments' => ['production', 'staging', 'local'],
```
要在测试套件中禁用检测,请设置 `APP_ENV=testing`(不在上面的列表中)或添加到你的 `phpunit.xml`:
```
```
### 配置参考
发布配置文件以查看所有可用选项:
```
php artisan vendor:publish --tag=threat-detection-config
```
关键配置部分:`skip_paths`(要跳过的路径)、`auth_paths`(登录路由的智能检测)、`content_paths`(抑制非高严重性警报)、`context_weights`(评分乘数)、`threat_levels`(严重性关键词映射)、`api_route_filtering`(在 API 路由上抑制低/中严重性警报)。
## Slack 通知
Slack 报警默认禁用。要启用:
```
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 webhooks**。无需额外的包。如果你偏好完整的通知通道,请安装:
```
composer require laravel/slack-notification-channel
```
## 仪表盘
该包附带内置的暗色模式仪表盘 (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`:
```
THREAT_DETECTION_DASHBOARD=true
```
访问:`http://your-app.test/threat-detection`
### 仪表盘认证
仪表盘默认使用 `['web', 'auth']` 中间件 —— 用户必须登录。
**如果你的应用尚未设置认证**(例如在本地开发期间),请在 `config/threat-detection.php` 中临时更改中间件:
```
'dashboard' => [
'enabled' => true,
'path' => 'threat-detection',
'middleware' => ['web'], // temporarily remove 'auth'
],
```
**如果仪表盘显示空数据**,请确保 API 端点可访问。仪表盘从 API 获取数据。详情请参阅 [API 认证](#api-authentication)。
## API 端点
该包提供 15 个 REST 端点,用于构建自定义仪表盘或集成。
### API 认证
API 路由默认使用 `auth:sanctum` 中间件。该包会优雅地处理此问题:
- **已安装 Sanctum:** API 需要通过 Sanctum token 或 SPA 会话认证。
- **未安装 Sanctum:** 该包**会自动检测** Sanctum 缺失并回退到仅使用 `['api']`。API 无需认证即可工作。
**如果你不使用 Sanctum 但想保护你的 API**,请在 `config/threat-detection.php` 中添加你自己的 auth guard:
```
'api' => [
'enabled' => true,
'prefix' => 'api/threat-detection',
'middleware' => ['api', 'auth'], // or 'auth:your-guard'
],
```
**用于本地测试**(如果 Sanctum 阻止访问),请临时更改:
```
'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-d/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`:**
```
{
"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:**
```
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:**
```
useEffect(() => {
fetch('/api/threat-detection/stats')
.then(res => res.json())
.then(data => setStats(data));
}, []);
```
## Artisan 命令
```
# 在终端中查看威胁统计摘要
php artisan threat-detection:stats
# 使用地理位置数据(国家、城市、ISP、云服务商)丰富现有日志
# 使用免费的 ip-api.com 服务 (速率限制为 45 请求/分钟,自动节流)
php artisan threat-detection:enrich --days=7
# 清除旧日志以保持数据库整洁
php artisan threat-detection:purge --days=30
```
## 自定义模式
在 `config/threat-detection.php` 中添加你自己的检测正则表达式模式:
```
'custom_patterns' => [
'/your-regex-here/i' => 'Your Threat Label',
],
```
**示例 — 检测对 WordPress 登录页面的请求:**
```
'/\/wp-login\.php/i' => 'WordPress Login Probe',
```
每个模式的威胁级别是通过将标签中的关键词与 `threat_levels` 配置进行匹配来自动确定的:
```
'threat_levels' => [
'high' => ['XSS', 'SQL Injection', 'RCE', 'Token', 'Password', 'Deserialization', 'Evasion', 'Encoding'],
'medium' => ['Directory Traversal', 'LFI', 'SSRF', 'Sensitive', 'Config', 'Recon Tool'],
'low' => ['User-Agent', 'Bot', 'Rate'],
],
```
如果标签不匹配任何关键词,威胁默认为 `low` 严重性。
无效的正则模式会被自动跳过并作为警告记录 —— 它们不会导致你的应用程序崩溃。
## 使用 Facade
用于在中间件之外以编程方式访问威胁数据:
```
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();
```
## 减少误报
### 内容路径抑制
如果你有 CMS 编辑器、博客文章表单或评论部分,用户在其中提交富文本内容,这些路径通常会触发误报(例如,包含 `