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…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…