achmdfzn/hippocrates
GitHub: achmdfzn/hippocrates
一款 Next.js 安全中间件,通过 Redis 累计威胁评分和蜜罐诱饵路由,在不暴露拦截信号的情况下隐蔽地隔离恶意请求。
Stars: 6 | Forks: 0
# Hippocrates
[](https://www.npmjs.com/package/hippocrates-middleware)
[](https://github.com/achmdfzn/hippocrates/actions/workflows/ci.yml)
[](https://codecov.io/gh/achmdfzn/hippocrates)
[](https://github.com/achmdfzn/hippocrates/blob/main/LICENSE)
[](https://github.com/achmdfzn/hippocrates)
Next.js App Router 安全中间件,它将恶意请求路由到诱饵 handler,而不是直接拦截它们。使用基于 Redis 的累计威胁评分系统,覆盖六个检测层。
```
npm install hippocrates-middleware zod
```
## 目录
- [工作原理](#how-it-works)
- [环境要求](#requirements)
- [教程](#tutorial)
- [代码库结构](#codebase-structure)
- [关联仓库](#pairing-the-repo)
- [防御层](#defense-layers)
- [配置](#configuration)
- [插件系统](#plugin-system)
- [事件钩子](#event-hooks)
- [统计跟踪](#stats-tracking)
- [ML 引擎 (Python Sidecar)](#ml-engine-python-sidecar)
- [许可证](#license)
## 工作原理
每个传入的请求都会经过检测层。每一层都会为存储在 Redis 中的 IP 累计威胁评分增加分数。如果分数超过阈值,该请求将被路由到诱饵生成器,而不是真实的 handler。诱饵会返回带有虚假数据的 200 OK 状态码。调用者永远不会看到 403 或 429,并且不会收到任何被检测到的信号。
```
Incoming Request
|
v
L-1 Allowlist? ---- YES --> Forward to handler (skip all checks)
| NO
v
L0 Pre-flight score check ---- score >= threshold? ---- YES --> HONEYPOT (200 OK, fake data)
| NO
v
Pre-body analyzers: L1 Timing, L2 Velocity, L3 UA, L4 Headers
| score >= threshold?
|-- YES --> HONEYPOT
| NO
v
Body parsing + Post-body analyzers: L5 Obfuscation, L6 Schema
| score >= threshold?
|-- YES --> HONEYPOT
| NO
v
Forward clean request to real handler
```
## 环境要求
- Node.js >= 18
- Next.js >= 14 (peer dependency)
- Zod >= 3.22 (peer dependency)
- Redis client (Upstash, ioredis 或兼容客户端)
## 教程
本教程将从头开始演示如何保护 Next.js App Router endpoint。
### 1. 创建或打开一个 Next.js 项目
```
npx create-next-app@latest my-app --typescript
cd my-app
```
### 2. 安装依赖
```
npm install hippocrates-middleware zod
npm install @upstash/redis # or your Redis client of choice
```
### 3. 设置路由
创建 `app/api/users/route.ts`:
```
import { NextRequest, NextResponse } from "next/server";
import { Redis } from "@upstash/redis";
import { withHippocrates, z } from "hippocrates-middleware";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// Schemas must use .strict() -- extra fields trigger a violation
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
}).strict();
async function handler(req: NextRequest): Promise {
const body = await req.json();
// body is already validated by the middleware
return NextResponse.json({ id: crypto.randomUUID(), ...body });
}
export const POST = withHippocrates(handler, CreateUserSchema, redis);
```
### 4. 添加环境变量
创建 `.env.local`:
```
UPSTASH_REDIS_REST_URL=https://your-redis-url.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-token
```
### 5. 运行
```
npm run dev
```
发送一个有效的请求:
```
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# 响应:200 OK 以及 { id, name, email }
```
中间件会根据 Zod schema 验证请求体。如果请求体有效,它将放行至 `handler`。
### 6. 触发蜜罐
发送一个带有额外字段的请求:
```
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com", "role": "admin"}'
```
`.strict()` schema 会拒绝未知字段。该请求将被路由到诱饵生成器,你会收到带有虚假数据的 200 OK 响应。
在短时间内从同一 IP 发送过多请求,或者使用带有可疑 User-Agent 的工具:
```
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-H "User-Agent: python-requests/2.31.0" \
-d '{"name": "Bob", "email": "bob@example.com"}'
```
UA 特征 `python-requests` 会触发 L3。在 10 秒内重复此操作 10 次以上,L2 频率跟踪将增加更多分数。一旦超过阈值,你就会进入蜜罐。
### 7. 使用自定义配置
```
export const POST = withHippocrates(handler, CreateUserSchema, redis, {
preset: "strict",
allowlist: { ips: ["10.0.0.0/8", "127.0.0.1"] },
bodyLimit: { maxBytes: 524288, enabled: true },
scoring: {
impossibleTiming: 35,
suspiciousUserAgent: 25,
},
hooks: {
onHoneypot: (event) => {
console.log(`Honeypot served to ${event.ip}`);
},
},
});
```
## 代码库结构
```
src/
index.ts # Entry point -- withHippocrates() HOF, re-exports
engine/
types.ts # Type definitions (RedisClient, HippocratesConfig, etc.)
constants.ts # Default values, UA patterns, obfuscation patterns
analyzers.ts # Built-in analyzer plugin placeholders (L1-L6)
threat-score-engine.ts # Redis-backed scoring engine with circuit breaker
system/
pipeline.ts # Request processing pipeline
honeypot.ts # Decoy response generator
validator.ts # Zod validation helpers (validatePayload, ensureStrict)
plugins/
ml-engine.ts # Python sidecar AnalyzerPlugin
utils/
ip.ts # IPv6 normalization and client IP resolution
__tests__/
helpers.ts # Test mocks
ip.test.ts # 30 tests
threat-score-engine.test.ts # 45 tests
validate-payload.test.ts # 8 tests
decoy.test.ts # 11 tests
with-hippocrates.test.ts # 51 tests (integration)
ensure-strict.test.ts # 25 tests
redis-degradation.test.ts # 6 tests
stats.test.ts # 5 tests
stats-integration.test.ts # 13 tests
ml-engine-integration.test.ts # 15 tests
engine-python/
app/
main.py # FastAPI application
config.py # Environment-based settings
models.py # Request/response models
analyzers/ # ML detection modules
prompt_injection.py
obfuscation_advanced.py
content_risk.py
tests/
test_analyzers.py # 31 tests
test_api.py # 8 tests
Dockerfile
requirements.txt
example/
app/api/data/route.ts # Reference implementation
```
## 关联仓库
```
git clone https://github.com/achmdfzn/hippocrates.git
cd hippocrates
npm install
npm run build
npm test # 209 tests
npm run typecheck # tsc --noEmit, zero errors
npm run lint # ESLint, zero errors
```
要运行包含 Python ML 引擎的完整技术栈:
```
# 需要:Docker、Python 3.12+
docker compose up -d # Redis + ML engine
npm test # TS tests (209)
cd engine-python && pytest -v # Python tests (39)
```
开发工作流:
```
npm run dev # tsup --watch (recompiles on source changes)
npm run test:watch # Vitest watch (reruns tests on changes)
```
CI pipeline (GitHub Actions):
```
quality (Node 18/20/22): lint -> typecheck -> test -> build
docker: build ML engine image -> healthcheck
```
## 防御层
| 层级 | 检查项 | 分数 | 条件 |
|-------|-------|--------|-----------|
| L-1 | IP 白名单 | 0 (全部绕过) | IP 在白名单配置中 |
| L0 | 预检分数 | 立即触发蜜罐 | 现有 Redis 分数 >= 阈值 |
| L1 | 请求时间 | +25 | 间隔 < 50ms |
| L2 | 请求频率 | +40 | 突发请求 > 15 次 / 10s 窗口 |
| L3 | User-Agent | +15 | 可疑或缺失 UA |
| L4 | HTTP 标头 | +15 | 缺失或使用通配符的 Accept 等 |
| L5 | Payload 混淆 | +100 | Base64、十六进制、URL 编码、Unicode |
| L6 | Zod schema | +100 | .strict() 违规 |
L5 和 L6 在检测到时会立即将分数提升至 100。
UA 特征 (40+ 种):LLM SDK (anthropic-sdk, openai-node, langchain),HTTP 库 (python-requests, curl, axios),浏览器自动化工具 (playwright, puppeteer),2026 AI agent (claude, cursor, perplexitybot, opencode)。
混淆特征:Base64 (>=24 字符),十六进制编码 (>=16 字符),URL 编码 (连续 5+ 字符),Unicode 转义,HTML 实体。
## 配置
```
interface HippocratesConfig {
preset?: "strict" | "moderate" | "relaxed";
threatScoreThreshold?: number; // Default: 65
velocityWindowMs?: number; // Default: 10000
velocityMaxRequests?: number; // Default: 15
threatTtlSeconds?: number; // Default: 3600
scoring?: Partial;
decoyGenerator?: (req: NextRequest) => Record;
debugMode?: boolean; // Default: false
plugins?: AnalyzerPlugin[];
hooks?: HippocratesHooks;
allowlist?: { ips: string[] };
bodyLimit?: { maxBytes: number; enabled: boolean };
methodThresholds?: Partial>;
violationMessages?: Record Record>;
statsTracker?: StatsTracker;
}
```
预设值:
| 预设 | 阈值 | 最大频率 | 窗口期 |
|--------|-----------|-------------|--------|
| strict | 40 | 10 次请求 | 10s |
| moderate | 65 | 15 次请求 | 10s |
| relaxed | 80 | 30 次请求 | 30s |
Redis 键结构:
| 键 | 用途 | TTL |
|-----|---------|-----|
| `hc:s:{ip}` | 威胁分数 (0-100) | `threatTtlSeconds` |
| `hc:t:{ip}` | 请求时间戳 (频率) | `windowMs + 10s` |
| `hc:l:{ip}` | 最后访问时间戳 (时间) | 300s |
自定义违规消息:
```
export const POST = withHippocrates(handler, schema, redis, {
violationMessages: {
obfuscation: (violation) => ({
error: "invalid_payload_format",
code: "OBFUSCATION_DETECTED",
}),
schema: (violation) => ({
error: "validation_failed",
}),
},
});
```
键是违规类型的前缀 (obfuscation、schema、ua、velocity、timing、header)。该函数接收完整的违规标签字符串,并返回一个与诱饵响应合并的对象。
## 插件系统
通过 AnalyzerPlugin 接口实现自定义检测逻辑:
```
import { type AnalyzerPlugin } from "hippocrates-middleware";
const geoBlock: AnalyzerPlugin = {
name: "geo_block",
phase: "pre-body", // "pre-body" | "post-body"
priority: 50, // Lower runs first. Default: 100
analyze(req, ctx) {
const country = req.headers.get("x-country");
if (country === "blocked") {
return { score: 50, tags: ["geo:blocked"] };
}
return { score: 0, tags: [] };
},
};
export const POST = withHippocrates(handler, schema, redis, {
plugins: [geoBlock],
});
```
插件在每个阶段内按 priority 升序排序。priority 相同则保持注册顺序。
其他用例示例:
**频率限制模拟 — 在不拦截的情况下为高频请求增加分数:**
```
const rateMimic: AnalyzerPlugin = {
name: "rate_mimic",
phase: "pre-body",
priority: 90,
analyze(req, ctx) {
const freq = parseInt(req.headers.get("x-request-frequency") ?? "0");
if (freq > 100) return { score: 30, tags: ["rate:high"] };
if (freq > 50) return { score: 15, tags: ["rate:medium"] };
return { score: 0, tags: [] };
},
};
```
**已知爬虫检测 — 匹配特定路由的 URL 模式:**
```
const scraperDetect: AnalyzerPlugin = {
name: "scraper_detect",
phase: "pre-body",
priority: 40,
analyze(req, ctx) {
const url = req.nextUrl.pathname;
const sensitivePaths = ["/api/users", "/api/orders", "/api/admin"];
if (sensitivePaths.some((p) => url.startsWith(p))) {
const ua = req.headers.get("user-agent") ?? "";
if (ua.includes("python-requests") || ua.includes("axios")) {
return { score: 25, tags: ["scraper:sensitive"] };
}
}
return { score: 0, tags: [] };
},
};
```
## 事件钩子
```
export const POST = withHippocrates(handler, schema, redis, {
hooks: {
onViolation: (event) => {
console.log(`${event.ip} - ${event.violations}`);
},
onPass: (event) => {
metrics.recordPass(event.ip, event.score);
},
onHoneypot: (event) => {
alertService.notify(`Honeypot served to ${event.ip}`);
},
},
});
```
## 统计跟踪
可通过 `ThreatScoreEngine.getStats()` 访问的内存计数器。传入自定义的 `StatsTracker` 以进行外部持久化:
```
import { type StatsTracker } from "hippocrates-middleware";
const tracker: StatsTracker = {
increment(counter) {
console.log(`Event: ${counter}`);
},
getStats() {
return { totalRequests: 0, blockedByPreflight: 0, /* ... */ };
},
reset() {},
};
```
警告:在 Serverless 环境 (Vercel Edge, AWS Lambda) 中,每次冷启动都会创建一个新的 `ThreatScoreEngine` 实例 —— 统计数据在每次调用时都会重置。请使用可持久化到外部存储的自定义 `StatsTracker` 以进行生产环境监控。
可用计数器:`totalRequests`、`blockedByPreflight`、`blockedByTiming`、`blockedByVelocity`、`blockedByObfuscation`、`blockedBySchema`、`passedToHandler`、`honeypotServed`、`redisErrors`。
基于 Redis 的 StatsTracker 示例:
```
import { type StatsTracker } from "hippocrates-middleware";
import { Redis } from "@upstash/redis";
function createRedisStatsTracker(redis: Redis): StatsTracker {
const key = "hc:stats";
return {
increment(counter) {
redis.hincrby(key, counter, 1).catch(() => {});
},
async getStats() {
const data = await redis.hgetall>(key);
if (!data) {
return {
totalRequests: 0, blockedByPreflight: 0, blockedByTiming: 0,
blockedByVelocity: 0, blockedByObfuscation: 0, blockedBySchema: 0,
passedToHandler: 0, honeypotServed: 0, redisErrors: 0,
};
}
return Object.fromEntries(
Object.entries(data).map(([k, v]) => [k, Number(v)]),
) as SecurityStats;
},
reset() {
redis.del(key).catch(() => {});
},
};
}
```
将其传递给 `withHippocrates` 以在冷启动间持久化统计数据:
```
export const POST = withHippocrates(handler, schema, redis, {
statsTracker: createRedisStatsTracker(redis),
});
```
## ML 引擎 (Python Sidecar)
可选的基于 ML 的检测:prompt 注入、高级混淆、内容风险评分 (SQLi、XSS、路径遍历、命令注入)。运行在 Python FastAPI sidecar 中。
```
docker compose up -d
```
```
import { mlEnginePlugin } from "hippocrates-middleware";
export const POST = withHippocrates(handler, schema, redis, {
plugins: [mlEnginePlugin({
baseUrl: "http://ml-engine:8000",
timeoutMs: 3000,
minScoreThreshold: 10,
})],
});
```
如果 ML 引擎不可达,它将返回分数 0 并带有 `ml-engine-unreachable` 标签。该插件有自己的熔断器:连续 3 次失败将触发 30 秒的冷却时间。
ML 引擎配置选项:
| 选项 | 默认值 | 描述 |
|--------|---------|-------------|
| `baseUrl` | `http://localhost:8000` | ML 引擎 endpoint |
| `timeoutMs` | `3000` | 请求超时时间 |
| `minScoreThreshold` | `10` | 贡献评分的最低 ML 分数 |
| `maxRetries` | `1` | 降级前的重试次数 |
| `circuitBreakerCooldownMs` | `30000` | 达到最大失败次数后的冷却时间 |
| `maxConsecutiveFailures` | `3` | 触发熔断前的失败次数 |
## 许可证
MIT (c) achmdfzn
标签:AppImage, CISA项目, MITM代理, Web安全, Web应用防火墙, 威胁评分, 安全中间件, 密码管理, 搜索引擎查询, 自动化攻击, 蓝队分析, 蜜罐技术, 请求拦截, 逆向工具