Skip to content

[BUG] ForwardedRemoteAddressResolver inverted guard discards client IP + blocking DNS on unvalidated X-Forwarded-For #6824

Description

@Aias00

Description

ForwardedRemoteAddressResolver (the production default, wired as new ForwardedRemoteAddressResolver(1) in ShenyuConfiguration) has an inverted guard in extractXForwardedValues: after splitting X-Forwarded-For by ", ", it does

if (values.size() == 1 && StringUtils.isNotEmpty(values.get(0))) {
    return Collections.emptyList();
}

isNotEmpty is the opposite of the intended check. The intent is to discard a single empty value (blank header), but the code discards a single non-empty value — i.e. a valid single client IP. resolve() then falls through to the TCP peer address (the proxy's IP), discarding the real client IP. Conversely, an empty-value header is not discarded and produces new InetSocketAddress("", 0) → wildcard 0.0.0.0.

Additionally resolve() constructs new InetSocketAddress(xForwardedValues.get(index), 0); the InetSocketAddress(String,int) ctor performs a blocking DNS lookup when the value is not a literal IP. X-Forwarded-For is unvalidated client input; with the default maxTrustedIndex=1 the leftmost (most spoofable) value is selected. When shenyu.scheduler.enabled=false (default), HostAddressUtils.acquireIp runs on the Netty event loop → blocking DNS stalls all connections on that thread (DoS) and enables DNS exfiltration/reconnaissance.

Location

  • shenyu-web/src/main/java/org/apache/shenyu/web/forward/ForwardedRemoteAddressResolver.java:89-108 (inverted guard :106-108; blocking DNS :89-90)
  • shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/gateway/ShenyuConfiguration.java:158-160 (production default)

Impact

  • Default deployment (single-hop trusted proxy appending one client IP — the most common case) silently ignores the client IP. HostAddressUtils.acquireIp(exchange) (consumed by AbstractLoggingPlugin for clientIp, and by any IP-based allow/deny/rate-limit feature) returns the proxy's IP. Access logs, rate limiting, and IP-based security all see the wrong IP.
  • DoS: crafted X-Forwarded-For hostnames trigger blocking DNS (up to system DNS timeout, 5–15s) on the event loop.
  • DNS exfiltration: gateway resolves attacker-controlled hostnames, leaking data to attacker's DNS server.

Suggested fix

  • Change StringUtils.isNotEmpty(values.get(0))StringUtils.isEmpty(values.get(0)) so only empty single values are discarded.
  • Validate the selected value is a literal IP (InetAddressUtils.isIPv4/IPv6 or try InetAddress.getByAddress) before constructing InetSocketAddress; otherwise fall back to the TCP remote address. Never call new InetSocketAddress(String,int) with unvalidated input on a reactive thread.

Related existing

None. Distinct from #6556 (WebSocket Upgrade header case-sensitivity in DefaultShenyuContextBuilder).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions