jay123anta/laravel-threat-detection

GitHub: jay123anta/laravel-threat-detection

一个Laravel中间件式威胁检测工具,通过130+正则模式实时识别40多种攻击类型,只记录不拦截,配套仪表盘、REST API和Slack告警。

Stars: 7 | Forks: 1

Latest Version Tests Total Downloads License PHP Version

# 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 编辑器、博客文章表单或评论部分,用户在其中提交富文本内容,这些路径通常会触发误报(例如,包含 `