Skip to content

Security: SSRF protection needed in HTTP task #1018

@nthmost-orkes

Description

@nthmost-orkes

Summary

The HTTP_TASK system task passes user-controlled URIs directly to restTemplate.exchange() with no restrictions on what hosts can be reached. This enables Server-Side Request Forgery (SSRF): anyone who can define or modify a workflow can instruct the Conductor server to make HTTP requests to internal infrastructure.

CodeQL Alert #2 · http-task/.../HttpTask.java:175 · severity: error

The Problem

// HttpTask.java:174-178
ResponseEntity<String> responseEntity =
        restTemplate.exchange(
                input.getUri(),   // ← fully user-controlled, no validation
                HttpMethod.valueOf(input.getMethod()),
                request,
                String.class);

There is no validation of input.getUri() before the call. An attacker with access to workflow definition APIs can target:

  • Cloud metadata endpointshttp://169.254.169.254/latest/meta-data/ (AWS IMDSv1, GCP, Azure) to steal instance credentials
  • RFC 1918 internal serviceshttp://10.x.x.x/, http://192.168.x.x/, http://172.16-31.x.x/
  • Loopbackhttp://localhost:8080/api/admin/... (Conductor's own admin API)
  • Internal DNS nameshttp://internal-db-host:5432/

Why This Is By-Design — and Still a Problem

The HTTP task is intentionally designed to call external endpoints — that's its entire purpose. The issue is the absence of any configurable guard rails. Most production deployments need to reach external services, but should not allow the Conductor server itself to become a proxy into its own internal network.

Proposed Solution

Add a configurable HttpTaskProperties class (or extend existing config) with:

  1. Block-by-default list: RFC 1918 ranges, loopback (127.0.0.0/8), link-local (169.254.0.0/16), and ::1
  2. Allowlist (conductor.system-task.http.allowed-hosts): explicit patterns that override the block list for legitimate internal calls
  3. Denylist (conductor.system-task.http.denied-hosts): additional patterns to block beyond defaults
  4. URI validation at the start of httpCall(), before restTemplate.exchange() is called

Example config:

conductor:
  system-task:
    http:
      deny-internal-network: true   # default: true
      allowed-hosts:
        - "api.trusted-partner.com"
      denied-hosts:
        - "*.internal.corp"

Threat Model Note

Risk is higher in multi-tenant or externally-accessible Conductor deployments where untrusted parties can submit workflow definitions. In fully internal, trusted-user-only deployments the practical risk is lower, but the fix is still correct defense-in-depth.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions