GHSA-GCFQ-8GQF-4876

Vulnerability from github – Published: 2026-07-02 13:50 – Updated: 2026-07-02 13:50
VLAI
Summary
GoFiber Vulnerable to X-Real-IP Spoofing via Header.Add() in BalancerForward
Details

Summary

The BalancerForward proxy helper in GoFiber uses Header.Add() instead of Header.Set() when injecting the X-Real-IP header. This appends the real client IP as a second header value rather than replacing any attacker-supplied value. Upstream servers that read the first X-Real-IP header (nginx, Express, most HTTP servers) use the attacker's spoofed IP for logging, rate limiting, and access control.

Vulnerable Code

File: middleware/proxy/proxy.go, lines 270-285

func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler {
    r := &roundrobin{
        current: 0,
        pool:    servers,
    }
    return func(c fiber.Ctx) error {
        server := r.get()
        if !strings.HasPrefix(server, "http") {
            server = "http://" + server
        }
        c.Request().Header.Add("X-Real-IP", c.IP())   // line 282: Add, not Set
        return Do(c, server+c.OriginalURL(), clients...)
    }
}

Data Flow

  1. Attacker sends request with X-Real-IP: 10.0.0.1 (spoofed internal IP)
  2. BalancerForward handler executes at line 282
  3. c.Request().Header.Add("X-Real-IP", c.IP()) APPENDS the real IP as a second header
  4. Upstream server receives: X-Real-IP: 10.0.0.1 AND X-Real-IP: <real-attacker-ip>
  5. Most HTTP servers (nginx, Node.js, Apache) read the FIRST value
  6. Upstream uses 10.0.0.1 for all IP-dependent logic

Impact

  • Rate limit bypass: IP-based rate limiting at the upstream uses the spoofed IP, allowing unlimited requests
  • IP ACL bypass: Internal IP allowlists (e.g., admin panels restricted to 10.0.0.0/8) can be bypassed
  • Audit log poisoning: Security logs record the spoofed IP, making incident investigation unreliable
  • Geolocation bypass: IP-based geofencing or region restrictions are circumvented

Fix

Replace Header.Add() with Header.Set() at line 282:

c.Request().Header.Set("X-Real-IP", c.IP())

Header.Set() replaces any existing header value, ensuring only the real client IP is forwarded.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.2.0"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/gofiber/fiber/v3"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/gofiber/fiber/v2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "2.52.13"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-45045"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-290"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-02T13:50:45Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThe `BalancerForward` proxy helper in GoFiber uses `Header.Add()` instead of `Header.Set()` when injecting the `X-Real-IP` header. This appends the real client IP as a second header value rather than replacing any attacker-supplied value. Upstream servers that read the first `X-Real-IP` header (nginx, Express, most HTTP servers) use the attacker\u0027s spoofed IP for logging, rate limiting, and access control.\n\n## Vulnerable Code\n\n**File:** `middleware/proxy/proxy.go`, lines 270-285\n\n```go\nfunc BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler {\n    r := \u0026roundrobin{\n        current: 0,\n        pool:    servers,\n    }\n    return func(c fiber.Ctx) error {\n        server := r.get()\n        if !strings.HasPrefix(server, \"http\") {\n            server = \"http://\" + server\n        }\n        c.Request().Header.Add(\"X-Real-IP\", c.IP())   // line 282: Add, not Set\n        return Do(c, server+c.OriginalURL(), clients...)\n    }\n}\n```\n\n## Data Flow\n\n1. Attacker sends request with `X-Real-IP: 10.0.0.1` (spoofed internal IP)\n2. `BalancerForward` handler executes at line 282\n3. `c.Request().Header.Add(\"X-Real-IP\", c.IP())` APPENDS the real IP as a second header\n4. Upstream server receives: `X-Real-IP: 10.0.0.1` AND `X-Real-IP: \u003creal-attacker-ip\u003e`\n5. Most HTTP servers (nginx, Node.js, Apache) read the FIRST value\n6. Upstream uses `10.0.0.1` for all IP-dependent logic\n\n## Impact\n\n- **Rate limit bypass:** IP-based rate limiting at the upstream uses the spoofed IP, allowing unlimited requests\n- **IP ACL bypass:** Internal IP allowlists (e.g., admin panels restricted to `10.0.0.0/8`) can be bypassed\n- **Audit log poisoning:** Security logs record the spoofed IP, making incident investigation unreliable\n- **Geolocation bypass:** IP-based geofencing or region restrictions are circumvented\n\n## Fix\n\nReplace `Header.Add()` with `Header.Set()` at line 282:\n\n```go\nc.Request().Header.Set(\"X-Real-IP\", c.IP())\n```\n\n`Header.Set()` replaces any existing header value, ensuring only the real client IP is forwarded.",
  "id": "GHSA-gcfq-8gqf-4876",
  "modified": "2026-07-02T13:50:45Z",
  "published": "2026-07-02T13:50:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gofiber/fiber/security/advisories/GHSA-gcfq-8gqf-4876"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gofiber/fiber"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "GoFiber Vulnerable to X-Real-IP Spoofing via Header.Add() in BalancerForward"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…