alperen-albayrak/koru

GitHub: alperen-albayrak/koru

这是一款采用自定义语义检测引擎解析上下文无关文法,旨在大幅降低误报率的开源 Web 应用防火墙。

Stars: 0 | Forks: 0

# Koru 开源 Web 应用防火墙,配备自定义语义检测引擎。将 SQL、HTML 和 JavaScript 解析为上下文无关文法——而非正则表达式——以实现近乎零的误报。 ![Go](https://img.shields.io/badge/Go-1.22-00ADD8?logo=go) ![License](https://img.shields.io/badge/License-MIT-green) ## Koru 是什么? Koru 是一个部署在 HTTP/HTTPS 后端前方的 WAF,会在请求到达您的应用程序之前检查每一个请求。 大多数 WAF(包括流行的开源选项)使用正则表达式来匹配攻击模式。问题在于:SQL 和 HTML 是上下文无关文法(Type-2)。正则表达式是 Type-3。它们无法正确解析这些语言,从而导致对合法输入的误报,以及通过编码技巧进行的绕过。 Koru 的检测引擎以真正的 SQL 解析器或浏览器的方式对输入进行分词和解析,然后分析生成的 token 流以检测恶意意图。这种方法借鉴了 [SafeLine](https://github.com/chaitin/SafeLine),据报道,其误报率为 0.07%,而基于正则表达式的引擎约为 17%。 **Koru 不是什么**:它不使用 Coraza、libinjection 或 OWASP 核心规则集。 ## 架构 ``` Internet │ ▼ koru-proxy (OpenResty compiled from source + Go supervisor) │ koru-supervisor: manages nginx lifecycle, syncs site config │ nginx: access_by_lua → T1K binary protocol over Unix socket ▼ koru-detector (Go) │ ├── Stage 1: Rate limit / IP block (Redis) ├── Stage 2: Recursive decoder (URL / HTML entity / hex / comments) ├── Stage 3: SQL injection analyzer (custom SQL lexer + token rules) ├── Stage 4: XSS analyzer (HTML5 tokenizer + JS lexer) ├── Stage 5: SSRF ├── Stage 6: SSTI ├── Stage 7: Command injection ├── Stage 8: Path traversal ├── Stage 9: Deserialization ├── Stage 10: File upload abuse └── Stage 11: CVE / scanner signatures (YAML rules) │ ├── Redis — rate limiting, IP block list └── PostgreSQL — async event log koru-mgt (Go REST API + gRPC) ──▶ koru-proxy supervisor (site config sync) koru-dashboard (React) ──▶ koru-mgt REST API ``` 代理容器完全从源代码构建——没有预构建的 nginx 镜像,没有外部 Lua 包。OpenResty 在 Dockerfile 构建阶段编译;T1K Lua 协议客户端是我们拥有的代码(`proxy/nginx/lua/t1k.lua`)。 检测使用**提前退出流水线**:一旦累计得分超过阻止阈值(默认为 50),请求就会被拒绝并返回 HTTP 403。不会运行后续阶段。 ## 快速开始 ``` git clone https://github.com/alperen-albayrak/koru cd koru cp koru.yaml.example koru.yaml # 编辑 koru.yaml 以匹配您的站点 docker compose -f docker/compose.yaml up -d ``` | 服务 | URL | 默认凭据 | |---|---|---| | Dashboard | http://localhost:8080 | admin / admin | | Management API | http://localhost:9443 | —(使用登录端点) | ## 基于文件的配置模式 如果您不需要 dashboard 或 API,可以使用单个 YAML 文件配置 Koru——就像配置 nginx 或 Caddy 一样。 ``` cp koru.yaml.example koru.yaml # 编辑 koru.yaml docker compose -f docker/compose.yaml -f docker/compose.file.yaml up -d ``` 对 `koru.yaml` 的更改会在 5 秒内自动生效——无需重启。此模式会从堆栈中省略 `koru-mgt` 和 `koru-dashboard`。 ## 站点配置 (`koru.yaml`) ``` sites: # ── Plain HTTP ──────────────────────────────────────────────────────────── - server_names: - example.com - www.example.com upstreams: - http://127.0.0.1:8080 ports: - "80" # ── HTTPS with TLS termination ──────────────────────────────────────────── - server_names: - secure.example.com upstreams: - http://127.0.0.1:8081 ports: - "443" tls: cert: /etc/ssl/certs/secure.example.com.crt key: /etc/ssl/private/secure.example.com.key # ── Multiple upstreams (round-robin) ───────────────────────────────────── - server_names: - api.example.com upstreams: - http://10.0.0.1:8080 - http://10.0.0.2:8080 ports: - "80" - "443" tls: cert: /etc/ssl/certs/api.example.com.crt key: /etc/ssl/private/api.example.com.key # ── Behind a load balancer using PROXY protocol ─────────────────────────── - server_names: - proxied.example.com upstreams: - http://127.0.0.1:8082 ports: - "80" proxy_protocol: true # ── Wildcard / catch-all ────────────────────────────────────────────────── - server_names: - "*" upstreams: - http://127.0.0.1:8090 ports: - "80" ``` | 字段 | 类型 | 必填 | 描述 | |---|---|---|---| | `server_names` | `[]string` | 是 | 要匹配的主机名。`"*"` 匹配任意内容。 | | `upstreams` | `[]string` | 是 | 后端 URL(`http://` 或 `https://`)。多个即为轮询。 | | `ports` | `[]string` | 是 | 要监听的端口(`"80"`, `"443"`)。 | | `tls.cert` | `string` | 否 | TLS 证书文件的绝对路径。 | | `tls.key` | `string` | 否 | TLS 私钥文件的绝对路径。 | | `proxy_protocol` | `bool` | 否 | 接受来自上游负载均衡器的 PROXY 协议。 | ## 部署拓扑 ### 直连(Koru 位于边缘) ``` Internet ──▶ Koru (port 80/443) ──▶ Backend ``` 无需额外配置。`$remote_addr` 已经是客户端 IP。 ### 位于反向代理之后 — X-Forwarded-For 当上游代理在 `X-Forwarded-For` 中追加真实的客户端 IP 时: ``` Client ──▶ Upstream proxy ──[X-Forwarded-For: ]──▶ Koru ──▶ Backend ``` 在 `koru-proxy` 上设置以下环境变量: ``` KORU_TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12 # CIDRs of upstream proxies to trust KORU_REAL_IP_HEADER=X-Forwarded-For # default — can also be X-Real-IP ``` Koru 使用 nginx 的 `ngx_http_realip_module`——它从指定的标头中读取真实 IP,并将其用于速率限制、事件日志记录和代理的 `$remote_addr`。 ### 位于负载均衡器之后 — PROXY 协议 当上游负载均衡器发送 PROXY 协议二进制标头时: ``` Client ──▶ Load balancer ──[PROXY protocol]──▶ Koru ──▶ Backend (X-Forwarded-For) ``` 1. 在 `koru.yaml` 中(或通过 API)为站点设置 `proxy_protocol: true`。 2. 生成的 nginx 配置会自动添加: listen 0.0.0.0:80 proxy_protocol; set_real_ip_from 0.0.0.0/0; real_ip_header proxy_protocol; 3. Koru 通过 `X-Real-IP` 和 `X-Forwarded-For` 将真实客户端 IP 转发给您的后端。 后端接收标准的 HTTP 标头——后端端无需特殊配置。 ## 检测引擎 ### 解码器 每个输入在分析前都会被规范化。解码器运行最多 8 遍,直到数值稳定: ``` URL decode → HTML entity decode → Hex string decode → SQL comment strip → Whitespace collapse → repeat ``` 这会在源头去除混淆。在词法分析器看到之前,`un/**/ion%20sel%65ct` 就会变成 `union select`。 ### SQL 注入 一个手写的 SQL 词法分析器生成 token 流(KEYWORD, IDENTIFIER, STRING, NUMBER, OPERATOR, PUNCT, FUNC, COMMENT)。分析器会查找 token 级别的模式: | 模式 | 示例 | 得分 | |---|---|---| | `UNION SELECT` | `1 UNION SELECT user,pass FROM users` | 80 | | 永真式 | `OR 1=1`, `'x'='x'` | 70 | | 堆叠查询 | `'; DROP TABLE users--` | 75 | | 基于时间的盲注 | `SLEEP(5)`, `BENCHMARK(...)` | 80 | | 基于错误的注入 | `EXTRACTVALUE(1,concat(...))` | 75 | | 子查询注入 | `(SELECT password FROM users)` | 60 | | 注释逃逸 | `ad/**/min` | 35 | | 危险函数 | `LOAD_FILE()`, `INTO OUTFILE` | 70–80 | ### XSS 一个 HTML5 状态机词法分析器跟踪整个输入的渲染上下文(文本、属性、脚本、样式)。一个独立的 JavaScript 词法分析器处理脚本上下文,包括递归模板字面量表达式(`${...}`)。 | 模式 | 示例 | 得分 | |---|---|---| | Script 标签 | `` | 90 | | 事件处理器 | `` | 85 | | `javascript:` URI | `href="javascript:alert(1)"` | 80 | | `data:` URI | `src="data:text/html,` | 70 | | CSS 表达式 | `style="x:expression(alert(1))"` | 65 | | `eval()` / `innerHTML` | `eval(atob('...'))` | 80–90 | ### 逻辑攻击 这些分析器对解码后的值进行操作: | 阶段 | 检测内容 | 示例 | |---|---|---| | SSRF | 私有 IP、危险的 URL 协议、云元数据端点 | `http://169.254.169.254/`, `file:///etc/passwd` | | SSTI | 模板引擎标记(Jinja2, Twig, Freemarker, Velocity, …) | `{{7*7}}`, `${7*7}` | | CMDi | Shell 元字符 + 分隔符序列 | `; cat /etc/passwd`, `$(id)` | | 路径遍历 | `../` 序列、空字节、敏感的绝对路径 | `../../../../etc/shadow` | | 反序列化 | Java 魔数、PHP serialize 格式、Python pickle、.NET ViewState | `rO0AB...` | | 文件上传 | Multipart:双重扩展名、内容/MIME 不匹配、正文中的 PHP/JSP | `.php.jpg`, `` | ### CVE 签名 启动时从 `detector/semantic/logic/cve/` 加载的 YAML 规则。每条规则都有一个 ID、正则模式、位置过滤器、得分和攻击类型: | 规则 | CVE | 得分 | |---|---|---| | Log4Shell | CVE-2021-44228 | 95 | | Spring4Shell | CVE-2022-22965 | 90 | | Shellshock | CVE-2014-6271 | 90 | | Struts2 OGNL | CVE-2017-5638 等 | 85 | | XXE | — | 85 | | PHP Webshell | — | 80 | | Security Scanner UA | — | 55 | ### 攻击类型 | 代码 | 名称 | 描述 | |---|---|---| | 1 | SQLi | SQL 注入 | | 2 | XSS | 跨站脚本 | | 3 | CMDi | 命令注入 | | 4 | SSRF | 服务器端请求伪造 | | 5 | SSTI | 服务器端模板注入 | | 6 | PathTraversal | 目录遍历 | | 7 | Deser | 反序列化 | | 8 | FileUpload | 恶意文件上传 | | 9 | CVE | 已知 CVE / 利用 | | 10 | Scanner | 安全扫描器指纹 | | 11 | RateLimit | 速率限制 / HTTP 异常 | ### 评分 每条规则贡献一个得分(0–100)。当请求的累计得分达到 `KORU_BLOCK_SCORE`(默认 `50`)时,该请求将被阻止并返回 HTTP 403。单个弱信号不会触发阻止——多个独立信号会累积。 ## 管理 API Base URL: `http://localhost:9443` 除 `/health` 和 `/auth/login` 外,所有端点都需要 `Authorization: Bearer `。 ### 认证 ``` POST /api/v1/auth/login Content-Type: application/json {"username": "admin", "password": "admin"} ``` ``` {"token": "eyJ..."} ``` ### 站点 | 方法 | 路径 | 描述 | |---|---|---| | `GET` | `/api/v1/health` | 存活探针(无需认证) | | `GET` | `/api/v1/sites` | 列出所有站点 | | `POST` | `/api/v1/sites` | 创建站点 | | `DELETE` | `/api/v1/sites/{id}` | 删除站点 | **创建站点请求体:** ``` { "server_names": ["example.com"], "upstreams": ["http://127.0.0.1:8080"], "ports": ["80"], "cert_filename": "", "key_filename": "", "proxy_protocol": false } ``` ### 事件 ``` GET /api/v1/events?limit=50&offset=0&site_id=1&attack_type=1&since=2024-01-01T00:00:00Z ``` | 参数 | 类型 | 默认值 | 描述 | |---|---|---|---| | `site_id` | int | — | 按站点筛选 | | `attack_type` | int | — | 按攻击类型筛选(1–11) | | `since` | RFC3339 | 24 小时前 | 时间范围开始 | | `until` | RFC3339 | — | 时间范围结束 | | `limit` | int | 50 | 最大结果数(硬性上限 200) | | `offset` | int | 0 | 分页偏移量 | ``` { "events": [ { "id": "uuid", "site_id": 1, "occurred_at": "2024-01-15T10:30:00Z", "src_ip": "1.2.3.4", "attack_type": 1, "risk_level": 2, "score": 80, "location": "query:id", "payload": "1 UNION SELECT user,password FROM users--" } ], "total": 142 } ``` ## 环境变量 ### koru-detector | 变量 | 默认值 | 描述 | |---|---|---| | `KORU_SOCKET` | `/run/koru/detector.sock` | Unix 套接字路径 | | `KORU_BLOCK_SCORE` | `50` | 阻止阈值(0–100) | | `KORU_MAX_BODY` | `1048576` | 要检查的最大请求体(字节) | | `KORU_LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` | | `KORU_REDIS_ADDR` | `koru-redis:6379` | Redis 地址 | | `KORU_RATE_LIMIT` | `500` | 每个 IP 每个时间窗口内允许的请求数 | | `KORU_RATE_WINDOW` | `60` | 速率限制时间窗口(秒) | | `KORU_BLOCK_DURATION` | `300` | IP 阻止时长(秒) | | `POSTGRES_HOST` | `koru-pg` | PostgreSQL 主机 | | `POSTGRES_PORT` | `5432` | PostgreSQL 端口 | | `POSTGRES_USER` | `koru` | PostgreSQL 用户 | | `POSTGRES_PASSWORD` | `korupass` | PostgreSQL 密码 | | `POSTGRES_DB` | `koru` | PostgreSQL 数据库 | ### koru-mgt | 变量 | 默认值 | 描述 | |---|---|---| | `KORU_HTTP_ADDR` | `:9443` | REST API 监听地址 | | `KORU_GRPC_ADDR` | `:9002` | gRPC 发布者监听地址 | | `KORU_JWT_SECRET` | `change-me-in-production` | JWT 签名密钥(请更改) | | `KORU_ADMIN_USER` | `admin` | 管理员用户 | | `KORU_ADMIN_PASS` | `admin` | 管理员密码(请更改) | | `POSTGRES_*` | 同上 | PostgreSQL 连接 | ### koru-proxy 代理容器是从源代码编译的 OpenResty,由 Go 监管进程管理。 | 变量 | 默认值 | 描述 | |---|---|---| | `KORU_MGT_ADDR` | `koru-mgt:9002` | 用于站点配置同步的 koru-mgt gRPC 地址(gRPC 模式) | | `KORU_CONFIG_FILE` | _(空)_ | `koru.yaml` 的路径;设置以启用文件模式(无需 mgt) | | `KORU_TRUSTED_PROXIES` | _(空)_ | 用于提取真实 IP 的受信任上游代理的 CIDR 列表,逗号分隔 | | `KORU_REAL_IP_HEADER` | `X-Forwarded-For` | 从中读取真实 IP 的标头:`X-Forwarded-For`、`X-Real-IP` 或 `proxy_protocol` | | `KORU_LOG_LEVEL` | `info` | 日志详细程度 | ## Dashboard Dashboard 位于 http://localhost:8080。它提供: - **登录** — JWT 认证,token 存储在浏览器会话中 - **仪表板** — 汇总卡片(今日事件、已阻止 IP、站点计数)+ 最近事件列表 - **站点** — 创建、查看和删除受保护的站点 - **事件** — 带有按攻击类型、站点和日期范围筛选功能的分页事件日志 ## 速率限制 Koru 在 Redis 中使用滑动窗口按 IP 跟踪请求。如果一个 IP 在 `KORU_RATE_WINDOW` 秒内超过 `KORU_RATE_LIMIT` 个请求,它将被阻止 `KORU_BLOCK_DURATION` 秒。来自同一 IP 的累计攻击事件达到 10 次后,阻止时长将增加 10 倍。 速率限制是故障开放的:如果 Redis 不可用,请求不会被速率限制器阻止(检测仍会运行)。 ## 从源代码构建 要求:Go 1.22+, pnpm 9+, Docker ``` # Proxy 主管 (Go) cd proxy && go build ./cmd/supervisor # Detector cd detector && go build ./cmd/detector # Management API cd mgt && go build ./cmd/mgt # Dashboard cd dashboard && pnpm install && pnpm build # 全部通过 Docker Compose(proxy 阶段从源码编译 OpenResty — 约 5 分钟) docker compose -f docker/compose.yaml build ``` ### 运行测试 ``` cd proxy && go test ./... cd detector && go test ./... cd mgt && go test ./... ``` ## 许可证 MIT
标签:AppImage, DoH影响, EVTX分析, Go语言, HTTP/HTTPS安全, Lua, Nginx, OpenResty, Rate Limiting, Redis, rizin, SafeLine, SQL注入检测, SSRF防护, SSTI防护, WAF, Web应用防火墙, XSS防护, 上下文无关文法, 反序列化, 命令注入, 安全网关, 搜索引擎查询, 无正则, 日志审计, 流量清洗, 测试用例, 程序破解, 网络安全, 词法分析, 语法解析, 误报率低, 请求拦截, 路径遍历, 隐私保护