/

CVE-2026-69246

Severity:

HIGH RISK

(7.2)

Host Validation Bypass (CWE-918) in Guzzle HTTP Client

CVE-2026-69246

Received

Vulnerability type

Category:

Server-Side Request Forgery (SSRF) and Host Validation Bypass

CWE:

CWE-180, CWE-436, CWE-918, CWE-941

Impact:

Bypass of host-based access controls and SSRF to restricted endpoints

Severity:

HIGH RISK

(7.2)

CVSS version:

3.1

Catch this CVE while code is being written

Detect vulnerable code directly in pull requests, so CVEs are fixed before merge.

Summary

A host validation bypass in the Guzzle PHP HTTP client allows libcurl and stream transports to connect to a different host than the one the application validated, due to inconsistencies in URI parsing and percent-decoding.

When applications build request URIs from untrusted input and rely on pre-Guzzle host checks, an attacker can craft special host spellings to reach excluded endpoints and read their responses.

Affected context

This vulnerability affects PHP applications that use Guzzle as an HTTP client and:

  • Construct request URIs from user-controlled or otherwise untrusted input
  • Perform host-based validation (e.g., IP literal checks, allowlists, blocklists) before handing the URI to Guzzle
  • Rely on Guzzle transports (cURL handlers or StreamHandler) to honor the same host spelling used in validation

It is most relevant in scenarios where Guzzle is used to fetch URLs on behalf of a user, perform webhook callbacks, or integrate with internal services, and host decisions are made on the textual URI rather than the final resolved authority.

Exploit preconditions

  • Attack surface: Application endpoints that accept or derive URLs, domains, or hosts from untrusted input and then fetch them via Guzzle.
  • Required access: Ability to influence the request URI (query parameters, JSON bodies, headers, configuration fields, or upstream data sources).
  • Trigger point: The moment Guzzle constructs and sends an HTTP request, passing the URI string into libcurl or fopen() while separately supplying a Host header.
  • Why exploitation is possible: Guzzle validates and reasons about the host using the literal URI string, while libcurl applies percent-decoding and IDNA mapping to derive the actual authority used for connection and TLS decisions. The divergence between application-level checks and transport-level parsing allows a crafted host spelling that fails validation but is still resolved and connected to, bypassing the intended host restrictions and related logic such as proxy selection and header stripping.

Why this happens

The core issue arises from inconsistent interpretation of URI authority between the application layer and the underlying HTTP transport.

Guzzle gives libcurl and stream handlers the request URI as written and supplies the Host header separately. Application code may run checks over the literal host string (for example, using filter_var() to reject IP literals or applying allowlists), assuming this host is the one actually contacted.

However, libcurl independently parses and normalizes the authority, performing percent-decoding and IDNA mapping. This means that hosts such as 127.0.0.%31 may be rejected by application-level filters yet decoded to 127.0.0.1 by libcurl and used to connect to loopback.

This behavioral mismatch maps to multiple CWEs:

  • CWE-918 (SSRF): The client can be induced to connect to internal or otherwise restricted hosts.
  • CWE-180 (Incorrect String Comparison): Host checks operate over the raw string, not the normalized value used by the transport.
  • CWE-436 (Interpretation Conflict): Different components interpret the same data differently, leading to security decisions based on inconsistent views.
  • CWE-941 (Incomplete Validation): Input validation fails to account for alternate encodings like percent-escaping and IDNA mapping.

Abstractly, the bug class is "security decision made on one representation of data while enforcement and execution use another representation," enabling attackers to bypass host-based controls without altering the apparent validation logic.

Potential impact

  • An attacker may cause Guzzle to connect to loopback (127.0.0.1) or other internal endpoints that the application attempted to exclude.
  • An attacker may read responses from services that should have been unreachable, exposing internal APIs, metadata endpoints, or administrative interfaces.
  • An attacker could influence proxy routing decisions, causing traffic to bypass expected proxies or security appliances.
  • Authorization or cookie-stripping logic may be misapplied, potentially preserving sensitive headers on redirects where they should have been removed.
  • Host-based access control policies may be undermined, weakening assumptions about what external resources the application can safely contact.

General mitigation guidance

Effective mitigation focuses on aligning host validation with the actual authority used by the transport and reducing trust in user-provided host data.

Recommended defensive patterns include:

  • Normalize and canonicalize hostnames and IP addresses before performing security checks, including decoding percent-encoded characters and applying appropriate IDNA processing where needed.
  • Centralize URL and host validation in a dedicated component that understands the same parsing rules as the HTTP transport, avoiding ad hoc checks scattered across the codebase.
  • Enforce strict allowlists for outbound destinations when performing server-side HTTP requests based on untrusted input, preferring explicit lists of safe domains or network ranges rather than negative filters.
  • Limit SSRF risk by constraining which schemes, ports, and address families can be used (for example, disallow loopback, link-local, and private addresses where not strictly required).
  • Separate user-visible hostnames from internal routing targets, so that external URLs are mapped to preconfigured internal endpoints instead of being used directly as connection parameters.
  • Apply least-privilege principles to outbound connectors, ensuring that even if host-based checks are bypassed, the client's network access is constrained by infrastructure-level controls such as firewalls or egress policies.

How teams detect issues like this

Detection typically involves analyzing both application-level URL handling and how Guzzle interacts with its transports.

Useful approaches include:

  • Static analysis rules that flag patterns where URLs from untrusted sources are passed directly into HTTP clients without robust normalization and allowlist checks, particularly when functions like filter_var() are used for IP validation without considering encoding tricks.
  • Code review signals such as:
  • Security decisions based on string comparisons of hosts or IPs.
  • Use of separate Host header manipulation alongside URI construction.
  • Logic that depends on host spelling for decisions like proxy routing or header stripping.
  • Configuration and policy checks that ensure outbound HTTP clients are constrained by network egress rules, preventing unrestricted access to internal ranges.
  • Runtime monitoring for suspicious outbound requests, such as traffic to loopback or private IP ranges triggered by endpoints that accept URLs or domains from users.
  • Adoption of automated security tooling or SAST platforms that understand SSRF patterns and can detect interpretation conflicts between application code and underlying libraries.

Sources

SourceLink
github.comgithub.com/guzzle/guzzle/commit/3aeea0406aab88cbbd86531313d7cebf8ae149a4
github.comgithub.com/guzzle/guzzle/commit/744101956d78b7c1384d0cbf379db13e859167bf
github.comgithub.com/guzzle/guzzle/pull/3907
github.comgithub.com/guzzle/guzzle/pull/3908
github.comgithub.com/guzzle/guzzle/releases/tag/7.15.2
github.comgithub.com/guzzle/guzzle/releases/tag/8.0.1
github.comgithub.com/guzzle/guzzle/security/advisories/GHSA-v5mv-p594-2x33

Frequently asked

questions

Exploitability depends on how the application uses Guzzle. It becomes significant when the application accepts or derives URLs from untrusted input and performs host-based checks before sending the request, trusting those checks to prevent connections to certain hosts or address ranges. In such designs, an attacker can often craft special host spellings to bypass the checks and influence which host Guzzle actually contacts.

An attacker must be able to influence the request URI that the application passes to Guzzle—typically via HTTP request parameters, JSON bodies, configuration entries, or upstream data feeds. No direct access to the underlying transport or network stack is required; control over the URL is sufficient when host validation relies on the raw string.

The safest approach is to stop making security decisions on unnormalized host strings and instead:

  • Canonicalize the host and scheme using parsing rules aligned with the HTTP client or transport.
  • Apply strict allowlists for permitted destinations, avoiding reliance on simple IP or hostname filters.
  • Design APIs so that users select from predefined endpoints rather than supplying arbitrary URLs.

These patterns minimize the gap between validation and actual connection behavior and reduce overall SSRF exposure.

Future designs should treat outbound HTTP requests based on user-controlled data as a high-risk operation. Preventive strategies include:

  • Normalizing and validating URL components with robust libraries that mirror transport behavior, including handling of percent-encoding and IDNs.
  • Using central security controls, such as internal routing services or egress filters, to enforce which destinations are reachable, independent of application logic.
  • Embedding SSRF-focused checks into code review and CI pipelines, with explicit guidelines discouraging ad hoc host comparisons.
  • Documenting and testing the interaction between HTTP client libraries and transports to ensure that application-level assumptions match how connections are actually established.
Find out what's already
exploitable in your codebase.