Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-47737

CVE-2026-47737: Puma Web Server Auth Bypass Vulnerability

CVE-2026-47737 is an authentication bypass flaw in Puma Ruby/Rack web server that enables source IP spoofing via PROXY protocol header injection. This article covers the technical details, affected versions, and mitigation.

Published:

CVE-2026-47737 Overview

CVE-2026-47737 is a source IP spoofing vulnerability in Puma, a Ruby/Rack web server built for parallelism. The flaw affects Puma versions from 5.5.0 up to (but not including) 7.2.1 and 8.0.2. When set_remote_address proxy_protocol: :v1 is enabled and persistent (keep-alive) connections are used, Puma re-parses PROXY protocol headers after each request on the same connection. An attacker can inject a second PROXY header into subsequent requests and overwrite REMOTE_ADDR, effectively spoofing the client source IP address. This weakness is classified as Authentication Bypass by Spoofing [CWE-290].

Critical Impact

Attackers can forge arbitrary source IP addresses, bypassing IP-based access controls, rate limiting, audit logging, and geo-restrictions in applications behind Puma.

Affected Products

  • Puma Ruby/Rack web server versions 5.5.0 through 7.2.0
  • Puma 8.0.0 and 8.0.1
  • Deployments using set_remote_address proxy_protocol: :v1 with keep-alive enabled

Discovery Timeline

  • 2026-07-14 - CVE-2026-47737 published to NVD
  • 2026-07-15 - Last updated in NVD database

Technical Details for CVE-2026-47737

Vulnerability Analysis

Puma supports the HAProxy PROXY protocol v1 to receive the true client IP from upstream load balancers. The PROXY header is intended to be sent once at the start of a TCP connection, before any HTTP data. Puma's client state machine, however, resets the @read_proxy flag on every request cycle when persistent connections are used. This causes the server to attempt to re-parse a PROXY header at the start of each pipelined or keep-alive HTTP request. An attacker who can open a direct connection to Puma, or who controls a request body on an existing connection, can prepend a crafted PROXY TCP4 ... line to a follow-up request. Puma treats the injected line as authoritative and overwrites REMOTE_ADDR for that request.

Root Cause

The root cause lies in lib/puma/client.rb, where the reset logic sets @read_proxy = !!@expect_proxy_proto without checking whether any request had already been served on the connection. Combined with a PROXY_PROTOCOL_V1_REGEX anchored with ^ (matching any line start) rather than \A (matching only the buffer start), Puma accepted PROXY headers appearing after the first request boundary.

Attack Vector

Exploitation requires network access to a Puma instance where PROXY protocol v1 parsing is enabled. The attacker establishes a keep-alive connection, sends a valid initial request, then sends a follow-up request whose bytes begin with a forged PROXY TCP4 <spoofed-ip> <dst-ip> <sport> <dport>\r\n line. Puma parses the forged header and reports the spoofed address to the Rack application via REMOTE_ADDR. This defeats controls that trust REMOTE_ADDR for authentication, allowlisting, or abuse mitigation.

ruby
# Security patch in lib/puma/client.rb (7.2.1 and 8.0.2 backports)
      @parser.reset
      @io_buffer.reset
      @read_header = true
-      @read_proxy = !!@expect_proxy_proto
+      @read_proxy = !!@expect_proxy_proto && @requests_served.zero?
      @env = @proto_env.dup
      @parsed_bytes = 0
      @ready = false

Source: GitHub Commit 439c6136

ruby
# Regex hardening in lib/puma/const.rb
    BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze

-    PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
+    PROXY_PROTOCOL_V1_REGEX = /\APROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
+    PROXY_PROTOCOL_V1_MAX_LENGTH = 107

Source: GitHub Commit ebe9db39

The fix restricts PROXY header parsing to the first request on a connection (@requests_served.zero?) and anchors the regex to the absolute start of the buffer to prevent mid-stream injection.

Detection Methods for CVE-2026-47737

Indicators of Compromise

  • Application or Rack access logs showing sudden shifts in REMOTE_ADDR for the same underlying TCP session or user agent fingerprint.
  • Requests where the client IP in application logs disagrees with upstream load balancer or WAF logs for the same request ID.
  • Presence of raw PROXY TCP4 or PROXY TCP6 byte sequences inside HTTP request bodies or headers captured in packet traces.

Detection Strategies

  • Compare REMOTE_ADDR values recorded by Puma against source IP fields logged by upstream reverse proxies, load balancers, or CDNs for the same request identifier.
  • Alert on any request whose first bytes on a persistent connection match the PROXY protocol v1 signature after a prior HTTP request on the same connection.
  • Inventory running Puma versions across Ruby application fleets and flag any instance in the 5.5.0 through 8.0.1 range that has set_remote_address proxy_protocol: :v1 configured.

Monitoring Recommendations

  • Enable connection-level logging in the upstream proxy to correlate true TCP source IPs against application-reported client IPs.
  • Track authentication events, rate-limit decisions, and admin actions keyed on REMOTE_ADDR for anomalous source distributions.
  • Ingest Puma and reverse-proxy logs into a centralized analytics platform to run continuous cross-source correlation queries.

How to Mitigate CVE-2026-47737

Immediate Actions Required

  • Upgrade Puma to version 7.2.1 or 8.0.2, which contain the official fix.
  • If immediate patching is not possible, disable set_remote_address proxy_protocol: :v1 in Puma configuration until the upgrade is complete.
  • Ensure Puma is not directly reachable from untrusted networks and only accepts connections from trusted upstream proxies.

Patch Information

The Puma maintainers released fixes in Puma v7.2.1 via Pull Request #3947 and Puma v8.0.2 via Pull Request #3944. Full technical detail is available in the GitHub Security Advisory GHSA-2vqw-3mp8-cgmx.

Workarounds

  • Disable PROXY protocol v1 parsing in Puma and rely on trusted X-Forwarded-For handling at the application layer with a validated proxy allowlist.
  • Terminate PROXY protocol at the upstream reverse proxy (for example, HAProxy or Nginx) and pass the client IP via a signed HTTP header that the application validates.
  • Restrict Puma's listening socket to a private network segment where only vetted load balancers can connect.
bash
# Upgrade Puma via Bundler to a patched release
bundle update puma --conservative
# Verify installed version is 7.2.1+ or 8.0.2+
bundle exec puma --version

# Temporary workaround: remove PROXY protocol v1 from puma config
# In config/puma.rb, comment out or remove:
#   set_remote_address proxy_protocol: :v1

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.