timescale/ressrf

GitHub: timescale/ressrf

多平台 SSRF 防御库,在发起网络连接前根据可配置策略验证目标地址,默认拦截私有网络与云元数据端点。

Stars: 3 | Forks: 0

ressrf

[![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/39/39faa54be350a1dab8afd3b2fb8c1c83e4d9cff84abfef2374d19a18053687c4.svg)](https://github.com/timescale/ressrf/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) 一个多平台 SSRF 防御库,具有经过模糊测试的 Rust 核心、可插拔的协议传输、可插拔的审计日志,并提供 Go、Python 和 Node.js 的绑定。 ressrf(发音同 "resurf")会在发起任何连接之前,根据可配置的拒绝/允许策略验证网络目标。默认情况下,它会阻止对私有网络、云元数据端点(AWS IMDS、Azure Wireserver、GCP metadata)、链路本地地址和其他内部网段的访问。协议适配器在 DNS 解析和建立连接阶段介入,为 HTTP 客户端、TCP 连接和 SSH 会话提供透明的保护。 ## 主要特性 - **拒绝优先的策略引擎**,内置预设、自定义允许/拒绝 CIDR 列表、URL 规则以及云服务提供商模块 - **默认拒绝列表**源自 IANA 特殊用途注册表,并通过每月自动更新保持最新 - 支持所有语言的**协议适配器**:HTTP(重定向重验证)、TCP(DNS 固定拨号)和 SSH - 通过简单的回调接口实现**可插拔的审计日志**,在所有绑定中保持一致 - 通过共享的 JSON 测试向量保证**跨语言一致性** - 使用 cargo-fuzz 进行**模糊测试**(每周由 CI 执行) ## 包 | Package | 语言 | 集成方式 | 文档 | |---------|----------|-------------|------| | [`ressrf-core`](crates/ressrf-core/) | Rust | 直接依赖 | [README](crates/ressrf-core/README.md) | | [`ressrf-tcp`](crates/ressrf-tcp/) | Rust | DNS 固定的 TCP 拨号 | [README](crates/ressrf-tcp/README.md) | | [`ressrf-http`](crates/ressrf-http/) | Rust | Tower Layer/Service | [README](crates/ressrf-http/README.md) | | [`ressrf-ssh`](crates/ressrf-ssh/) | Rust | russh/async-ssh2 的 Guard | [README](crates/ressrf-ssh/README.md) | | [`ressrf-tracing`](crates/ressrf-tracing/) | Rust | TracingSink 审计适配器 | [README](crates/ressrf-tracing/README.md) | | [`ressrf-wasm`](crates/ressrf-wasm/) | Rust | 用于 Go/Node.js 的 WASM ABI | [README](crates/ressrf-wasm/README.md) | | [`go/ressrf`](go/ressrf/) | Go | wazero WASM runtime | [README](go/ressrf/README.md) | | [`go-native/ressrf`](go-native/ressrf/) | Go | 原生移植(无 WASM,无 CGO) | [README](go-native/ressrf/README.md) | | [`python/`](python/) | Python | PyO3 原生扩展 | [README](python/README.md) | | [`node/`](node/) | TypeScript | WebAssembly API | [README](node/README.md) | ``` ┌─────────────────────────┐ │ ressrf-core │ │ (policy, CIDR, URI, │ │ audit, cloud, trie) │ ┌──────────────────────┐ └───────┬─────────────────┘ │ Go (native) │ │ │ pure-Go port │ ┌─────────────────┼─────────────────┐ │ │ │ │ │ │ Consumes only the │ ▼ ▼ ▼ │ shared JSON: │ ┌────────────────┐ ┌─────────────┐ ┌────────────────┐ │ • config/*.json │ │ ressrf-wasm │ │ ressrf-http │ │ ressrf-ssh │ │ • tests/vectors │ │ (WASM ABI) │ │ (Tower) │ │ (russh) │ │ │ └───────┬────────┘ └──────┬──────┘ └───────┬────────┘ │ No Rust dependency; │ │ │ │ │ pinned to ressrf- │ ┌─────┴─────┐ └───────┬─────────┘ │ core via differen- │ │ │ │ │ tial fuzz against │ ▼ ▼ ▼ │ ressrf-wasm. │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ └──────────────────────┘ │Go(wazero)│ │ Node.js │ │ Python │ │ binding │ │ (WASM) │ │ (PyO3) │ └──────────┘ └──────────┘ └──────────────┘ ``` 原生 Go 移植版位于 [`go-native/ressrf/`](go-native/ressrf/)。它非常适合那些倾向于使用原生调试能力(`pprof`、`delve`)以及不希望将 Rust 工具链引入 PR 贡献流程的 Go 团队。 ## 快速开始 ### Rust ``` use ressrf_core::{PolicyBuilder, UriValidator}; let policy = PolicyBuilder::external_only().build(); assert!(policy.is_network_allowed(&["10.0.0.1".parse().unwrap()]).is_err()); assert!(policy.is_network_allowed(&["93.184.216.34".parse().unwrap()]).is_ok()); ``` ### Go(wazero,共享 Rust 引擎) ``` policy, _ := ressrf.NewPolicyBuilder(ressrf.PresetExternalOnly). WithCloudProviders("aws", "azure", "gcp"). Build(ctx) defer policy.Close(ctx) err := policy.IsAllowed(ctx, "http://169.254.169.254/latest/meta-data/") // err: blocked ``` ### Go(原生) ``` policy, _ := ressrf.NewPolicy(ressrf.PresetExternalOnly, ressrf.WithCloudProviderDenies(ressrf.CloudAWS, ressrf.CloudAzure, ressrf.CloudGCP), ) err := policy.IsAllowed(ctx, "http://169.254.169.254/latest/meta-data/") // err: blocked ``` ### Python ``` from ressrf import Policy, RessrfBlockedError policy = Policy.external_only(cloud=["aws"]) try: policy.validate_url("http://169.254.169.254/latest/meta-data/") except RessrfBlockedError as e: print(f"Blocked: {e.reason}") ``` ### Node.js ``` import { Policy, isBlocked } from "ressrf"; const policy = await Policy.externalOnly({ cloud: ["aws"] }); try { policy.isAllowed("http://169.254.169.254/latest/meta-data/"); } catch (err) { if (isBlocked(err)) console.log("Blocked:", err.reason); } ``` ## 安装 | 语言 | 命令 | 要求 | |----------|---------|--------------| | Rust | `cargo add ressrf-core` | Rust 1.75+ | | Go (wazero) | `go get github.com/timescale/ressrf/go/ressrf` | Go 1.26+ | | Go (原生) | `go get github.com/timescale/ressrf/go-native/ressrf` | Go 1.25+ | | Python | `pip install ressrf` | Python 3.10+ | | Node.js | `npm install ressrf` | Node.js 20+ | 请参阅各个包的 README 了解可选的附加功能(协议适配器、功能标志)。 ## 允许列表 允许规则会覆盖拒绝规则。您可以为特定的 CIDR 打通孔洞,同时保持对其余私有地址空间的拦截: ``` // Rust PolicyBuilder::external_only().add_allowed(&["10.42.0.0/16"]).build(); ``` ``` // Go (wazero) NewPolicyBuilder(PresetExternalOnly).WithAllowedCIDRs("10.42.0.0/16").Build(ctx) ``` ``` // Go (native) ressrf.NewPolicy(ressrf.PresetExternalOnly, ressrf.WithAllowedCIDRs("10.42.0.0/16")) ``` ``` # Python Policy.external_only(allowed=["10.42.0.0/16"]) ``` ``` // Node.js await Policy.externalOnly({ allowCidrs: ["10.42.0.0/16"] }); ``` ## URL 规则 用于在基于 CIDR 的过滤之外,实现 URL 级别的允许/拒绝。规则对主机使用 glob 模式(`*` = 单个 DNS 标签),对路径也使用 glob 模式(`*` = 单个分段,`**` = 任意深度),并可选择使用 regex 处理复杂模式: ``` // Rust PolicyBuilder::external_only() .url_allow(UrlRule::glob("https", "*.stripe.com", "/v1/**")) .url_deny(UrlRule::host("*.internal")) .build(); ``` ``` // Go (wazero) NewPolicyBuilder(PresetExternalOnly). WithURLAllow(URLRule{Scheme: "https", Host: "*.stripe.com", Path: "/v1/**"}). WithURLDeny(URLRule{Host: "*.internal"}). Build(ctx) ``` ``` // Go (native) ressrf.NewPolicy(ressrf.PresetExternalOnly, ressrf.WithURLAllow(ressrf.URLRuleGlob("https", "*.stripe.com", "/v1/**")), ressrf.WithURLDeny(ressrf.URLRuleGlob("", "*.internal", "")), ) ``` ``` # Python PolicyBuilder("external_only") \ .url_allow(scheme="https", host="*.stripe.com", path="/v1/**") \ .url_deny(host="*.internal") \ .build() ``` ``` // Node.js new PolicyBuilder("external_only") .urlAllow({ scheme: "https", host: "*.stripe.com", path: "/v1/**" }) .urlDeny({ host: "*.internal" }) .build(); ``` 系统会首先检查拒绝规则。当配置了允许规则时,任何不匹配允许规则的 URL 都将被拦截。在允许规则上设置 `bypass_ip_check: true` 可以跳过对受信任端点的 IP 级别检查。 ## 审计日志 所有绑定都暴露了相同的可插拔接口。该库会输出结构化事件,但绝不指定必须使用哪种日志框架: ``` // Rust: implement the AuditSink trait let policy = PolicyBuilder::external_only() .audit_sink(Box::new(my_sink)) .build(); ``` ``` // Go: any function works sink := ressrf.AuditFunc(func(ctx context.Context, e *ressrf.AuditEvent) { slog.InfoContext(ctx, "ressrf", "kind", e.Kind) }) ``` ``` # Python: 任何 callable 均可 sink = AuditFunc(lambda event: print(f"[{event.event_type}] {event.fields}")) ``` ``` // Node.js: any object with emit() works const sink = new AuditFunc((event) => console.log(event.kind, event.fields)); ``` ## IP 范围代码生成 `scripts/generate_ip_ranges.py` 会从 IANA、AWS、Azure 和 GCP 获取上游 IP 范围数据。每月一次的 CI 工作流会验证更改并自动发起 PR。服务范围可在运行时通过 `ServiceRangeTable` 进行查询(基于字典树,O(log n) 查询复杂度)。 ``` python scripts/generate_ip_ranges.py # full update python scripts/generate_ip_ranges.py --iana-only # skip cloud service ranges python scripts/generate_ip_ranges.py --validate-only ``` ## 测试 `tests/vectors/` 中的共享测试向量确保了所有语言下的行为完全一致,其中包括一个包含 92 个测试用例的 `ssrf_techniques.json`,涵盖了完整的 SSRF 绕过技术分类(IP 表示技巧、IPv6 变体、解析器混淆、协议走私、云元数据、Unicode/IDN 等): ``` cargo test --workspace --all-features # Rust cd go/ressrf && go test -race ./... # Go (wazero) cd go-native/ressrf && go test -race ./... # Go (native) cd python && uv run pytest tests/ -v # Python cd node && npx tsx --test tests/*.test.ts # Node.js ``` 位于 `crates/ressrf-tcp/tests/ssrf_e2e.rs` 的 Tier 2 端到端测试套件,利用 CoreDNS 和 WireMock 容器(`tests/containers/`),在整个网络协议栈中测试了 DNS 重绑定固定和重定向链。该测试受 `e2e` Cargo feature 限制且需要 Docker;当 Docker 不可用时,测试将附带提示并自动跳过: ``` cargo test --features e2e -p ressrf-tcp --test ssrf_e2e ``` ## CI/CD - **Rust:** check、fmt、clippy、test (Linux/macOS/Windows)、WASM 构建 - **Go (wazero + 原生):** test (多操作系统、竞态检测器)、vet、golangci-lint;原生移植版还会额外运行针对 wazero 绑定的 WASM 预言机的差分模糊测试任务 - **Python:** pytest (多操作系统)、ruff、ty - **Node.js:** node:test (多操作系统)、tsc - **SSRF e2e:** 仅限 Linux 的 Tier 2 任务会启动 CoreDNS + WireMock,以端到端验证基于 DNS 和重定向的绕过行为 - **安全:** cargo audit、govulncheck、cargo-fuzz (每周)、zizmor - **IP 范围:** 每月一次的上游数据获取、验证、测试以及自动发起 PR ## 贡献 如果您希望看到与我们集成了您最喜欢的编程语言、云服务提供商、协议或库,欢迎发起 issue 或提交 pull request。我们欢迎各种形式的贡献。 请参阅 [HACKING.md](HACKING.md) 了解开发环境设置、测试,以及添加新云服务提供商、语言绑定、协议适配器和客户端库集成的分步指南。 ## 许可证 MIT
标签:AI工具, MITM代理, Rust, SSRF防护, TLS, 可视化界面, 多语言绑定, 日志审计, 网络安全, 网络流量审计, 网络访问控制, 逆向工具, 通知系统, 防御工具, 隐私保护