SSRF Protection#
RAGFlow implements defense-in-depth against Server-Side Request Forgery (SSRF) through a centralized guard module applied across HTTP, database, and crawling request paths. The core primitive is common/ssrf_guard.py, a stdlib-only module importable from both api/ and common/ without heavyweight dependencies.
Core Guard Functions#
common/ssrf_guard.py exposes two validation functions:
-
assert_url_is_safe(url, *, allowed_schemes)— For HTTP/HTTPS targets. Validates scheme, resolves the hostname viagetaddrinfo, and rejects any address that is not globally routable (ip.is_global). This allowlist approach blocks loopback, private (RFC 1918), link-local, multicast, cloud metadata (169.254.169.254), and all other special-purpose ranges in one check. Returns(hostname, resolved_ip)for subsequent DNS pinning. -
assert_host_is_safe(host)— For raw host/port connections (database drivers, non-HTTP protocols). Performs the same IP-globalness check without URL parsing.
An escape hatch exists via the ALLOW_ANY_HOST environment variable , which disables IP validation while emitting a warning — intended only for development environments.
DNS Pinning / Rebinding Prevention#
Validating a hostname and then connecting to it creates a TOCTOU window: an attacker can change DNS between the guard check and the actual TCP connection. RAGFlow closes this with socket-level DNS pinning :
pin_dns(hostname, ip)— Thread-local context manager for synchronousrequests.get()callers. Pins the validated IP in the current thread only.pin_dns_global(hostname, ip)— Process-global context manager for async callers (e.g.,crawl4ai-based crawlers) where DNS resolution happens in thread-pool executor threads that don't share thread-local state.
Both work by monkey-patching socket.getaddrinfo at import time so that pinned hostnames are always resolved to the pre-validated IP, making rebinding attacks ineffective.
IPv4-mapped IPv6 addresses (e.g., ::ffff:127.0.0.1) are normalized to their IPv4 form via _effective_ip() before the globalness check, preventing bypass via IPv6 representation.
Protected Request Paths#
| Component | Function called | Source |
|---|---|---|
| Agent HTTP Invoke component | assert_url_is_safe + pin_dns (URL + proxy) | invoke.py |
| Agent ExeSQL tool (DB connections) | assert_host_is_safe | agent/tools/exesql.py |
test_db_connection REST endpoint | assert_host_is_safe | api/apps/restful_apis/agent_api.py |
| Web crawler agent tool | assert_url_is_safe + pin_dns_global | agent/tools/crawler.py |
| Document upload URL fetch | assert_url_is_safe | api/db/services/file_service.py |
| SearXNG agent tool | assert_url_is_safe + pin_dns | agent/tools/searxng.py |
| RSS connector | assert_url_is_safe (feed + redirect chain) | common/data_source/rss_connector.py |
| REST API connector | assert_url_is_safe + pin_dns | common/data_source/rest_api_connector.py |
| MCP API | assert_url_is_safe | api/apps/restful_apis/mcp_api.py |
| Naive RAG URL fetch | assert_url_is_safe + pin_dns | rag/app/naive.py |
| Document API | assert_url_is_safe | api/apps/restful_apis/document_api.py |
| Web utils | assert_url_is_safe | api/utils/web_utils.py |
Invoke Component Details#
The agent Invoke component applies SSRF guards at both the target URL and any configured proxy URL before issuing a request :
- The URL is resolved and validated in
_build_url(), storing the pinned hostname and IP on the instance. - If a proxy is configured, its URL is also validated with
assert_url_is_safe. - The actual request runs inside nested
pin_dns()context managers for both the target and proxy . - Redirects are disabled via
allow_redirects=Falseto prevent redirect-based SSRF bypass.
PR History#
| PR | Change |
|---|---|
| #14090 | Created common/ssrf_guard.py; applied to document crawling, RSS, SearXNG, web utils |
| #14860 | Added assert_host_is_safe(); guarded test_db_connection REST endpoint |
| #15426 | Added SSRF guard + DNS pinning to the agent Invoke HTTP component |
| #15609 | Guarded ExeSQL tool's DB host across all 6 database driver branches |