GHSA-3Q9R-P662-5J8M

Vulnerability from github – Published: 2026-08-06 16:38 – Updated: 2026-08-06 16:38
VLAI
Summary
Traefik: ForwardAuth middleware leaks X-Forwarded-Port spoofing via untrusted X-Forwarded-Proto when trustForwardHeader=false
Details

Summary

There is a medium severity vulnerability in Traefik's ForwardAuth middleware. Even when configured with trustForwardHeader: false, Traefik derives the X-Forwarded-Port header sent to the authentication service from the original incoming request instead of the sanitized forwarded request. As a result, an unauthenticated remote attacker can inject an X-Forwarded-Proto: https header over a plain HTTP connection and cause Traefik to forward X-Forwarded-Port: 443 to the auth service, bypassing port-based authorization checks. This is a regression of the incomplete fix for GHSA-6384-m2mw-rf54, which addressed the X-Forwarded-Proto and X-Forwarded-Prefix spoofing vectors but missed the X-Forwarded-Port vector.

Patches

  • https://github.com/traefik/traefik/releases/tag/v2.11.51
  • https://github.com/traefik/traefik/releases/tag/v3.6.22
  • https://github.com/traefik/traefik/releases/tag/v3.7.6

For more information

If you have any questions or comments about this advisory, please open an issue.

Original Description ### Summary The ForwardAuth middleware, even when configured with `trustForwardHeader: false`, still derives the `X-Forwarded-Port` header sent to the authentication service by reading the **attacker-controlled** `X-Forwarded-Proto` header from the original incoming request. This allows an unauthenticated remote attacker to cause Traefik to forward `X-Forwarded-Port: 443` to the auth service on a plain HTTP connection, creating an inconsistency that can bypass port-based authorization checks. ### Details The fix introduced in commit `5e1de2258` (released as part of the April 2026 security advisory GHSA-6384-m2mw-rf54) correctly strips all X-Forwarded-* headers from the forwarded auth request when `trustForwardHeader=false`, and reconstructs `X-Forwarded-Proto` from the actual TLS state of the connection (`req.TLS`). However, the reconstruction of `X-Forwarded-Port` is delegated to the helper `forwardedPort(req)` which receives the **original request** (`req`) rather than the sanitized forward request (`forwardReq`): ```go // pkg/middlewares/auth/forward.go – writeHeader() if !trustForwardHeader { forwardedheaders.DeleteXForwardedHeaders(forwardReq.Header) // strips all X-Fwd-* from forwardReq } // ... if _, ok := forwardReq.Header[forwardedheaders.XForwardedPort]; !ok { forwardReq.Header.Set(forwardedheaders.XForwardedPort, forwardedPort(req)) // ← req = ORIGINAL } // pkg/middlewares/auth/forward.go – forwardedPort() func forwardedPort(req *http.Request) string { if _, port, err := net.SplitHostPort(req.Host); err == nil && port != "" { return port } // Reads attacker-controlled header on the ORIGINAL request: if req.Header.Get(forwardedheaders.XForwardedProto) == "https" || ... { return "443" } if req.TLS != nil { return "443" } return "80" } Result when trustForwardHeader=false and attacker sends X-Forwarded-Proto: https on a plain HTTP connection: ┌──────────────────────────────────┬──────────┬────────┐ │ Header forwarded to auth service │ Expected │ Actual │ ├──────────────────────────────────┼──────────┼────────┤ │ X-Forwarded-Proto │ http │ http ✓ │ ├──────────────────────────────────┼──────────┼────────┤ │ X-Forwarded-Port │ 80 │ 443 ✗ │ └──────────────────────────────────┴──────────┴────────┘
  The inconsistency between Proto=http and Port=443 is exploitable against any
  authentication service that gates access based on X-Forwarded-Port.

  ### PoC

  Traefik configuration:

  ```http:
    middlewares:
      my-auth:
        forwardAuth:
          address: "http://auth-service/"
          trustForwardHeader: false  # security setting, but still bypassable
    routers:
      api:
        rule: "PathPrefix(`/api`)"
        middlewares:
          - my-auth
        service: backend

  Auth service logic (example victim):
  # auth-service checks: only port 443 requests are considered "secure"
  port = request.headers.get("X-Forwarded-Port", "80")
  proto = request.headers.get("X-Forwarded-Proto", "http")
  if port == "443":
      return 200  # grant access
  return 403
Attack: Plain HTTP connection, no TLS – but spoofs port 443 curl -H "X-Forwarded-Proto: https" http://traefik.example.com/api/admin Auth service receives X-Forwarded-Port: 443 → grants access Verification: Enable Traefik debug logging and observe X-Forwarded-Port: 443 in the auth request while the connection is plain HTTP. ### Impact Any deployment using the ForwardAuth middleware with trustForwardHeader: false where the downstream authentication service uses X-Forwarded-Port to make authorization decisions is vulnerable to privilege escalation. An unauthenticated attacker can bypass port-based security checks (e.g., "only allow requests arriving on HTTPS port 443") by injecting a single X-Forwarded-Proto: https header on a plain HTTP connection. This is a regression of the incomplete fix for GHSA-6384-m2mw-rf54: while the X-Forwarded-Prefix and X-Forwarded-Proto spoofing vectors were addressed, the X-Forwarded-Port vector was missed.
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.11.50"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/traefik/traefik/v2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.11.51"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.6.21"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/traefik/traefik/v3"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.6.22"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.7.5"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/traefik/traefik/v3"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.7.0"
            },
            {
              "fixed": "3.7.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/traefik/traefik"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "1.7.34"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54764"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-345"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-06T16:38:29Z",
    "nvd_published_at": "2026-07-06T21:16:56Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThere is a medium severity vulnerability in Traefik\u0027s ForwardAuth middleware. Even when configured with `trustForwardHeader: false`, Traefik derives the `X-Forwarded-Port` header sent to the authentication service from the original incoming request instead of the sanitized forwarded request. As a result, an unauthenticated remote attacker can inject an `X-Forwarded-Proto: https` header over a plain HTTP connection and cause Traefik to forward `X-Forwarded-Port: 443` to the auth service, bypassing port-based authorization checks. This is a regression of the incomplete fix for GHSA-6384-m2mw-rf54, which addressed the `X-Forwarded-Proto` and `X-Forwarded-Prefix` spoofing vectors but missed the `X-Forwarded-Port` vector.\n\n## Patches\n\n- https://github.com/traefik/traefik/releases/tag/v2.11.51\n- https://github.com/traefik/traefik/releases/tag/v3.6.22\n- https://github.com/traefik/traefik/releases/tag/v3.7.6\n\n## For more information\n\nIf you have any questions or comments about this advisory, please [open an issue](https://github.com/traefik/traefik/issues).\n\n\u003cdetails\u003e\n\u003csummary\u003eOriginal Description\u003c/summary\u003e\n\n### Summary\n\n  The ForwardAuth middleware, even when configured with `trustForwardHeader: false`,\n  still derives the `X-Forwarded-Port` header sent to the authentication service by\n  reading the **attacker-controlled** `X-Forwarded-Proto` header from the original\n  incoming request. This allows an unauthenticated remote attacker to cause Traefik\n  to forward `X-Forwarded-Port: 443` to the auth service on a plain HTTP connection,\n  creating an inconsistency that can bypass port-based authorization checks.\n\n  ### Details\n\n  The fix introduced in commit `5e1de2258` (released as part of the April 2026 security\n  advisory GHSA-6384-m2mw-rf54) correctly strips all X-Forwarded-* headers from the\n  forwarded auth request when `trustForwardHeader=false`, and reconstructs\n  `X-Forwarded-Proto` from the actual TLS state of the connection (`req.TLS`).\n\n  However, the reconstruction of `X-Forwarded-Port` is delegated to the helper\n  `forwardedPort(req)` which receives the **original request** (`req`) rather than\n  the sanitized forward request (`forwardReq`):\n\n  ```go\n  // pkg/middlewares/auth/forward.go \u2013 writeHeader()\n  if !trustForwardHeader {\n      forwardedheaders.DeleteXForwardedHeaders(forwardReq.Header) // strips all X-Fwd-* from forwardReq\n  }\n  // ...\n  if _, ok := forwardReq.Header[forwardedheaders.XForwardedPort]; !ok {\n      forwardReq.Header.Set(forwardedheaders.XForwardedPort, forwardedPort(req)) // \u2190 req = ORIGINAL\n  }\n\n  // pkg/middlewares/auth/forward.go \u2013 forwardedPort()\n  func forwardedPort(req *http.Request) string {\n      if _, port, err := net.SplitHostPort(req.Host); err == nil \u0026\u0026 port != \"\" {\n          return port\n      }\n      // Reads attacker-controlled header on the ORIGINAL request:\n      if req.Header.Get(forwardedheaders.XForwardedProto) == \"https\" || ... {\n          return \"443\"\n      }\n      if req.TLS != nil {\n          return \"443\"\n      }\n      return \"80\"\n  }\n\n  Result when trustForwardHeader=false and attacker sends X-Forwarded-Proto: https\n  on a plain HTTP connection:\n\n  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n  \u2502 Header forwarded to auth service \u2502 Expected \u2502 Actual \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 X-Forwarded-Proto                \u2502 http     \u2502 http \u2713 \u2502\n  \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n  \u2502 X-Forwarded-Port                 \u2502 80       \u2502 443 \u2717  \u2502\n  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n  The inconsistency between Proto=http and Port=443 is exploitable against any\n  authentication service that gates access based on X-Forwarded-Port.\n\n  ### PoC\n\n  Traefik configuration:\n\n  ```http:\n    middlewares:\n      my-auth:\n        forwardAuth:\n          address: \"http://auth-service/\"\n          trustForwardHeader: false  # security setting, but still bypassable\n    routers:\n      api:\n        rule: \"PathPrefix(`/api`)\"\n        middlewares:\n          - my-auth\n        service: backend\n\n  Auth service logic (example victim):\n  # auth-service checks: only port 443 requests are considered \"secure\"\n  port = request.headers.get(\"X-Forwarded-Port\", \"80\")\n  proto = request.headers.get(\"X-Forwarded-Proto\", \"http\")\n  if port == \"443\":\n      return 200  # grant access\n  return 403\n```\n  Attack:\n\n  Plain HTTP connection, no TLS \u2013 but spoofs port 443\n  curl -H \"X-Forwarded-Proto: https\" http://traefik.example.com/api/admin\n  Auth service receives X-Forwarded-Port: 443 \u2192 grants access\n\n  Verification: Enable Traefik debug logging and observe X-Forwarded-Port: 443\n  in the auth request while the connection is plain HTTP.\n\n ### Impact\n\n  Any deployment using the ForwardAuth middleware with trustForwardHeader: false where\n  the downstream authentication service uses X-Forwarded-Port to make authorization\n  decisions is vulnerable to privilege escalation. An unauthenticated attacker can\n  bypass port-based security checks (e.g., \"only allow requests arriving on HTTPS port\n  443\") by injecting a single X-Forwarded-Proto: https header on a plain HTTP\n  connection.\n\n  This is a regression of the incomplete fix for GHSA-6384-m2mw-rf54: while the\n  X-Forwarded-Prefix and X-Forwarded-Proto spoofing vectors were addressed, the\n  X-Forwarded-Port vector was missed.\n\n\u003c/details\u003e\n\n---",
  "id": "GHSA-3q9r-p662-5j8m",
  "modified": "2026-08-06T16:38:29Z",
  "published": "2026-08-06T16:38:29Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/security/advisories/GHSA-3q9r-p662-5j8m"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54764"
    },
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/pull/13344"
    },
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/commit/7ae92d8c2c10ac04ef5a03df0ed5019ce0f44b2d"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/traefik/traefik"
    },
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/releases/tag/v2.11.51"
    },
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/releases/tag/v3.6.22"
    },
    {
      "type": "WEB",
      "url": "https://github.com/traefik/traefik/releases/tag/v3.7.6"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:L/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Traefik: ForwardAuth middleware leaks X-Forwarded-Port spoofing via untrusted X-Forwarded-Proto when trustForwardHeader=false"
}



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…

Loading…