dify - 💡(How to fix) Fix [Security] get_api_tool_provider_remote_schema() bypasses ssrf_proxy, enables authenticated SSRF (CVSS 5.3)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Dify's get_api_tool_provider_remote_schema() endpoint fetches OpenAPI schemas from user-supplied URLs using a direct httpx.get() call, bypassing the project's SSRF protection proxy (ssrf_proxy). While all other external URL fetching in Dify correctly routes through ssrf_proxy, this endpoint was missed.

Error Message

from httpx import get

class ApiToolManageService: @classmethod def get_api_tool_provider_remote_schema(cls, tenant_id: str, args: UrlQuery) -> dict: try: # try to parse schema, avoid SSRF attack ← comment suggests SSRF awareness, but no proxy used response = get(str(args.url), timeout=10) response.raise_for_status() schema = parser_api_schema(response.json()) except Exception as e: raise ValueError(f"Failed to parse remote schema: {e}") return {"schema": schema}

Root Cause

Dify's get_api_tool_provider_remote_schema() endpoint fetches OpenAPI schemas from user-supplied URLs using a direct httpx.get() call, bypassing the project's SSRF protection proxy (ssrf_proxy). While all other external URL fetching in Dify correctly routes through ssrf_proxy, this endpoint was missed.

Fix Action

Fix / Workaround

Mitigations:

  • URL is validated via Pydantic HttpUrl (only http/https allowed, blocks file://, gopher://, etc.)
  • Requires authentication (3 layers)
  • Response must be parseable as OpenAPI schema (though error messages may leak information)

Code Example

from httpx import get

class ApiToolManageService:
    @classmethod
    def get_api_tool_provider_remote_schema(cls, tenant_id: str, args: UrlQuery) -> dict:
        try:
            # try to parse schema, avoid SSRF attack  ← comment suggests SSRF awareness, but no proxy used
            response = get(str(args.url), timeout=10)
            response.raise_for_status()
            schema = parser_api_schema(response.json())
        except Exception as e:
            raise ValueError(f"Failed to parse remote schema: {e}")
        return {"schema": schema}

---

from core.helper.ssrf_proxy import SSRFProxy

class ApiToolManageService:
    @classmethod
    def get_api_tool_provider_remote_schema(cls, tenant_id: str, args: UrlQuery) -> dict:
        try:
            response = SSRFProxy.get(str(args.url), timeout=10)  # Use SSRF proxy
            response.raise_for_status()
            schema = parser_api_schema(response.json())
        except Exception as e:
            raise ValueError(f"Failed to parse remote schema: {e}")
        return {"schema": schema}
RAW_BUFFERClick to expand / collapse

Dify: SSRF via get_api_tool_provider_remote_schema() Bypasses ssrf_proxy

Summary

Dify's get_api_tool_provider_remote_schema() endpoint fetches OpenAPI schemas from user-supplied URLs using a direct httpx.get() call, bypassing the project's SSRF protection proxy (ssrf_proxy). While all other external URL fetching in Dify correctly routes through ssrf_proxy, this endpoint was missed.

Affected Versions

  • langgenius/dify (all versions with API tool provider feature)
  • api/services/tools/api_tools_manage_service.py

CVSS v4.0: 5.3 (Medium)

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:L/SI:N/SA:N

Details

File: api/services/tools/api_tools_manage_service.py

from httpx import get

class ApiToolManageService:
    @classmethod
    def get_api_tool_provider_remote_schema(cls, tenant_id: str, args: UrlQuery) -> dict:
        try:
            # try to parse schema, avoid SSRF attack  ← comment suggests SSRF awareness, but no proxy used
            response = get(str(args.url), timeout=10)
            response.raise_for_status()
            schema = parser_api_schema(response.json())
        except Exception as e:
            raise ValueError(f"Failed to parse remote schema: {e}")
        return {"schema": schema}

Contrast with other services that correctly use ssrf_proxy:

  • app_dsl_service.py → uses ssrf_proxy.get()
  • external_knowledge_service.py → uses ssrf_proxy.get()
  • rag_pipeline_dsl_service.py → uses ssrf_proxy.get()
  • file_factory/remote.py → uses ssrf_proxy.get()

Attack Chain:

  1. Authenticated user (requires @login_required + @account_initialization_required + @setup_required)
  2. Calls GET /console/api/workspaces/current/tool-provider/api/remote-schema?url=http://169.254.169.254/latest/meta-data/
  3. Server makes direct httpx.get() to the target URL (bypasses ssrf_proxy)
  4. Response content is returned as {"schema": <content>} → internal network information disclosure

Mitigations:

  • URL is validated via Pydantic HttpUrl (only http/https allowed, blocks file://, gopher://, etc.)
  • Requires authentication (3 layers)
  • Response must be parseable as OpenAPI schema (though error messages may leak information)

Remediation

Replace httpx.get() with ssrf_proxy.get() to maintain consistency with the rest of the codebase:

from core.helper.ssrf_proxy import SSRFProxy

class ApiToolManageService:
    @classmethod
    def get_api_tool_provider_remote_schema(cls, tenant_id: str, args: UrlQuery) -> dict:
        try:
            response = SSRFProxy.get(str(args.url), timeout=10)  # Use SSRF proxy
            response.raise_for_status()
            schema = parser_api_schema(response.json())
        except Exception as e:
            raise ValueError(f"Failed to parse remote schema: {e}")
        return {"schema": schema}

References

  • CWE-918: Server-Side Request Forgery (SSRF)

Discoverer

IcySun & Yashon [email protected]

CVE Request

We request CVE assignment for this vulnerability.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING