GHSA-W6J9-VW59-27WV

Vulnerability from github – Published: 2026-06-22 17:09 – Updated: 2026-07-21 13:16
VLAI
Summary
Gogs has an Authentication Bypass via Unvalidated Reverse Proxy Headers
Details

Summary

When ENABLE_REVERSE_PROXY_AUTHENTICATION is enabled, Gogs accepts the configured authentication header (default: X-WEBAUTH-USER) directly from client requests without validating that the request originated from a trusted reverse proxy. Any remote attacker who can reach the Gogs service can forge this header to impersonate any user or trigger automatic account creation, completely bypassing authentication.

Root Cause

The vulnerability exists because Gogs reads the authentication header directly from the incoming HTTP request without any verification that the header was set by a trusted reverse proxy.

Vulnerable Code Flow

In internal/context/auth.go lines 206-234:

func authenticatedUser(store AuthStore, ctx *macaron.Context, sess session.Store) (_ *database.User, isBasicAuth, isTokenAuth bool) {
    // ... existing auth checks ...

    if uid <= 0 {
        if conf.Auth.EnableReverseProxyAuthentication {
            // Reads header DIRECTLY from client request - NO VALIDATION!
            webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)
            if len(webAuthUser) > 0 {
                user, err := store.GetUserByUsername(ctx.Req.Context(), webAuthUser)
                if err != nil {
                    if !database.IsErrUserNotExist(err) {
                        log.Error("Failed to get user by name: %v", err)
                        return nil, false, false
                    }

                    // Check if enabled auto-registration.
                    if conf.Auth.EnableReverseProxyAutoRegistration {
                        // Creates new user with forged username!
                        user, err = store.CreateUser(
                            ctx.Req.Context(),
                            webAuthUser,
                            gouuid.NewV4().String()+"@localhost",
                            database.CreateUserOptions{
                                Activated: true,
                            },
                        )
                        if err != nil {
                            log.Error("Failed to create user %q: %v", webAuthUser, err)
                            return nil, false, false
                        }
                    }
                }
                // Returns user as authenticated without any verification!
                return user, false, false
            }
        }
        // ... fallback to basic auth ...
    }
    // ...
}

The code has zero validation that: 1. The request came through a reverse proxy 2. The header was set by the proxy (not the client) 3. Gogs is actually behind a reverse proxy 4. The direct access to Gogs is restricted

The vulnerability occurs when: - Gogs is publicly accessible (e.g., 0.0.0.0:3000) - ENABLE_REVERSE_PROXY_AUTHENTICATION = true

Proof of Concept

Prerequisites

Gogs instance with the following configuration in custom/conf/app.ini:

[auth]
ENABLE_REVERSE_PROXY_AUTHENTICATION = true

An attacker can impersonate any user including administrators:

# Become admin instantly
curl http://gogs.example.com/ -H "X-WEBAUTH-USER: <username>"

impersonation_example

Recommended Fixes

Add validation to ensure headers come from trusted sources:

func authenticatedUser(store AuthStore, ctx *macaron.Context, sess session.Store) (_ *database.User, isBasicAuth, isTokenAuth bool) {
    // ... existing code ...

    if uid <= 0 {
        if conf.Auth.EnableReverseProxyAuthentication {
            // Validate request is from trusted proxy
            if !isRequestFromTrustedProxy(ctx.Req) {
                log.Warn("Reverse proxy auth header received from untrusted source: %s", ctx.RemoteAddr())
                return nil, false, false
            }

            webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)
            // ... rest of the code ...
        }
    }
    // ...
}

// New validation function
func isRequestFromTrustedProxy(req *http.Request) bool {
    // Check if request is from localhost/trusted IPs
    remoteIP := getRemoteIP(req)

    // Only accept from localhost by default
    if remoteIP.IsLoopback() {
        return true
    }

    // Check against configured trusted proxy IPs
    for _, trustedIP := range conf.Auth.TrustedProxyIPs {
        if remoteIP.String() == trustedIP {
            return true
        }
    }

    return false
}

Add configuration option:

[auth]
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
REVERSE_PROXY_AUTHENTICATION_HEADER = X-WEBAUTH-USER
; Comma-separated list of trusted proxy IPs (default: 127.0.0.1)
TRUSTED_PROXY_IPS = 127.0.0.1,::1
; Whether to require trusted proxy validation (recommended: true)
REQUIRE_TRUSTED_PROXY = true

References

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.14.2"
      },
      "package": {
        "ecosystem": "Go",
        "name": "gogs.io/gogs"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.14.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-25119"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-290"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-22T17:09:51Z",
    "nvd_published_at": "2026-06-24T21:16:52Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nWhen `ENABLE_REVERSE_PROXY_AUTHENTICATION` is enabled, Gogs accepts the configured authentication header (default: `X-WEBAUTH-USER`) directly from client requests without validating that the request originated from a trusted reverse proxy. Any remote attacker who can reach the Gogs service can forge this header to impersonate any user or trigger automatic account creation, completely bypassing authentication.\n\n## Root Cause\n\nThe vulnerability exists because Gogs reads the authentication header directly from the incoming HTTP request without any verification that the header was set by a trusted reverse proxy.\n\n### Vulnerable Code Flow\n\nIn `internal/context/auth.go` lines 206-234:\n\n```go\nfunc authenticatedUser(store AuthStore, ctx *macaron.Context, sess session.Store) (_ *database.User, isBasicAuth, isTokenAuth bool) {\n    // ... existing auth checks ...\n\n    if uid \u003c= 0 {\n        if conf.Auth.EnableReverseProxyAuthentication {\n            // Reads header DIRECTLY from client request - NO VALIDATION!\n            webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)\n            if len(webAuthUser) \u003e 0 {\n                user, err := store.GetUserByUsername(ctx.Req.Context(), webAuthUser)\n                if err != nil {\n                    if !database.IsErrUserNotExist(err) {\n                        log.Error(\"Failed to get user by name: %v\", err)\n                        return nil, false, false\n                    }\n\n                    // Check if enabled auto-registration.\n                    if conf.Auth.EnableReverseProxyAutoRegistration {\n                        // Creates new user with forged username!\n                        user, err = store.CreateUser(\n                            ctx.Req.Context(),\n                            webAuthUser,\n                            gouuid.NewV4().String()+\"@localhost\",\n                            database.CreateUserOptions{\n                                Activated: true,\n                            },\n                        )\n                        if err != nil {\n                            log.Error(\"Failed to create user %q: %v\", webAuthUser, err)\n                            return nil, false, false\n                        }\n                    }\n                }\n                // Returns user as authenticated without any verification!\n                return user, false, false\n            }\n        }\n        // ... fallback to basic auth ...\n    }\n    // ...\n}\n```\n\nThe code has **zero validation** that:\n1. The request came through a reverse proxy\n2. The header was set by the proxy (not the client)\n3. Gogs is actually behind a reverse proxy\n4. The direct access to Gogs is restricted\n\nThe vulnerability occurs when:\n- Gogs is publicly accessible (e.g., `0.0.0.0:3000`)\n- `ENABLE_REVERSE_PROXY_AUTHENTICATION = true`\n\n## Proof of Concept\n\n### Prerequisites\n\nGogs instance with the following configuration in `custom/conf/app.ini`:\n\n```ini\n[auth]\nENABLE_REVERSE_PROXY_AUTHENTICATION = true\n```\n\nAn attacker can impersonate any user including administrators:\n\n```bash\n# Become admin instantly\ncurl http://gogs.example.com/ -H \"X-WEBAUTH-USER: \u003cusername\u003e\"\n```\n\n\u003cimg width=\"1835\" height=\"1143\" alt=\"impersonation_example\" src=\"https://github.com/user-attachments/assets/bae60772-5eb3-4f54-9fe0-5db01595bd56\" /\u003e\n\n## Recommended Fixes\n\nAdd validation to ensure headers come from trusted sources:\n\n```go\nfunc authenticatedUser(store AuthStore, ctx *macaron.Context, sess session.Store) (_ *database.User, isBasicAuth, isTokenAuth bool) {\n    // ... existing code ...\n\n    if uid \u003c= 0 {\n        if conf.Auth.EnableReverseProxyAuthentication {\n            // Validate request is from trusted proxy\n            if !isRequestFromTrustedProxy(ctx.Req) {\n                log.Warn(\"Reverse proxy auth header received from untrusted source: %s\", ctx.RemoteAddr())\n                return nil, false, false\n            }\n\n            webAuthUser := ctx.Req.Header.Get(conf.Auth.ReverseProxyAuthenticationHeader)\n            // ... rest of the code ...\n        }\n    }\n    // ...\n}\n\n// New validation function\nfunc isRequestFromTrustedProxy(req *http.Request) bool {\n    // Check if request is from localhost/trusted IPs\n    remoteIP := getRemoteIP(req)\n\n    // Only accept from localhost by default\n    if remoteIP.IsLoopback() {\n        return true\n    }\n\n    // Check against configured trusted proxy IPs\n    for _, trustedIP := range conf.Auth.TrustedProxyIPs {\n        if remoteIP.String() == trustedIP {\n            return true\n        }\n    }\n\n    return false\n}\n```\n\nAdd configuration option:\n\n```ini\n[auth]\nENABLE_REVERSE_PROXY_AUTHENTICATION = false\nREVERSE_PROXY_AUTHENTICATION_HEADER = X-WEBAUTH-USER\n; Comma-separated list of trusted proxy IPs (default: 127.0.0.1)\nTRUSTED_PROXY_IPS = 127.0.0.1,::1\n; Whether to require trusted proxy validation (recommended: true)\nREQUIRE_TRUSTED_PROXY = true\n```\n\n## References\n\n- [CWE-290: Authentication Bypass by Spoofing](https://cwe.mitre.org/data/definitions/290.html)\n- [OWASP: Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html)\n- [OWASP Top 10 2021 - A07: Identification and Authentication Failures](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/)",
  "id": "GHSA-w6j9-vw59-27wv",
  "modified": "2026-07-21T13:16:36Z",
  "published": "2026-06-22T17:09:51Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/security/advisories/GHSA-w6j9-vw59-27wv"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25119"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/pull/8264"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/commit/0089c4c8e5b8d99eb6e5c8727f8f40d765f1f58a"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gogs/gogs"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gogs/gogs/releases/tag/v0.14.3"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Gogs has an Authentication Bypass via Unvalidated Reverse Proxy Headers"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

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.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
How durations are calculated

Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.

Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.

Events included in the timeline

Loading timeline…


    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.