Skip to content

X-Forwarded-Prefix open redirect

Moderate
vrana published GHSA-8478-xrj3-h9c2 Jul 17, 2026

Package

composer vrana/adminer (Composer)

Affected versions

4.6.0 - 5.4.4

Patched versions

5.5.0

Description

Summary

The new reverse-proxy support prepends the client-supplied X-Forwarded-Prefix header to $_SERVER["REQUEST_URI"] on every request, with no configuration gate and no validation. Because REQUEST_URI always begins with /, an attacker header like X-Forwarded-Prefix: https://evil.example yields a fully attacker-controlled absolute URL that then flows into Location: redirects, the Set-Cookie path attribute, and every self-referential link Adminer emits.

Severity

Medium. Authenticated open redirect (state-changing table/edit POSTs), plus unauthenticated control of the session-cookie path attribute and of self-referential URLs, from a single spoofable request header. The header is honoured even when Adminer is not behind a reverse proxy.

Root Cause

adminer/include/bootstrap.inc.php:40-42:

if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
    $_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"];
}

There is no check that the request actually came through a trusted proxy and no restriction on the prefix value. The poisoned REQUEST_URI then reaches:

  • redirect() / queries_redirect()header("Location: $location") where $location is $_SERVER["REQUEST_URI"] (e.g. adminer/db.inc.php:52, adminer/edit.inc.php:20).
  • cookie_path()adminer/include/functions.inc.php:324, used for the session cookie (bootstrap.inc.php:49) and all Set-Cookie paths.

Impact

  • Open redirect: an authenticated state-changing POST (e.g. truncate/analyze/optimize in db.inc.php) returns Location: https://evil.example/..., sending the operator off-site after the action.
  • Session-cookie path control (pre-auth): the attacker sets the path= attribute of adminer_sid, enabling cookie-scoping tricks / session fixation on shared hosts.
  • Self-URL poisoning: links and form targets built from the poisoned URI point at the attacker's prefix on the rendered page.
  • CR/LF cannot be injected (PHP header() strips them), so this is not header splitting or XSS.

Reproduction Steps

Environment

  • OS: Kali Linux, kernel 6.19.14+kali-amd64; PHP 8.4.21
  • Adminer 5.4.5-dev

Steps

  1. Model the poisoning with the exact bootstrap + sink code:
    php poc_2_xforwarded_prefix.php
    Expected: with X-Forwarded-Prefix: https://evil.example, redirect() emits Location: https://evil.example/adminer/?... and cookie_path() returns https://evil.example/adminer/.
  2. Live: send any request with X-Forwarded-Prefix: https://evil.example; observe self-links and the Set-Cookie: adminer_sid=...; path= reflect the prefix. For the redirect, submit an authenticated table op POST and inspect the Location response header.

Proof of Concept Code

<?php
// Adminer 5.4.5-dev — X-Forwarded-Prefix poisons REQUEST_URI. Models bootstrap.inc.php:40-42
// and the two sinks (redirect Location + cookie_path) with the exact source lines.

// --- attacker input ---
$_SERVER["REQUEST_URI"] = "/adminer/?server=db&username=root&db=x";
$_SERVER["HTTP_X_FORWARDED_PREFIX"] = "https://evil.example";   // spoofable request header

// --- bootstrap.inc.php:40-42 (verbatim) ---
if ($_SERVER["HTTP_X_FORWARDED_PREFIX"]) {
    $_SERVER["REQUEST_URI"] = $_SERVER["HTTP_X_FORWARDED_PREFIX"] . $_SERVER["REQUEST_URI"];
}
echo "poisoned REQUEST_URI = " . $_SERVER["REQUEST_URI"] . "\n";

// --- sink 1: redirect() Location (functions.inc.php:441, e.g. db.inc.php:52 passes REQUEST_URI) ---
$location = $_SERVER["REQUEST_URI"];
echo "Location: $location   <-- open redirect off-site\n";

// --- sink 2: cookie_path() (functions.inc.php:324), used for the session cookie path ---
$cookie_path = strtr(preg_replace('~\?.*~', '', $_SERVER["REQUEST_URI"]), array(";" => "%3B", "," => "%2C"));
echo "Set-Cookie: adminer_sid=...; path=$cookie_path   <-- attacker-set cookie path\n";

Patch Recommendation

  • Only honor X-Forwarded-Prefix when the request originates from a configured trusted proxy (explicit opt-in setting), matching how frameworks gate X-Forwarded-*.
  • Require the prefix to be a path (must start with /, no scheme/authority) and reject anything containing :// or a leading //.

Affected Files

  • adminer/include/bootstrap.inc.php:40-42
  • adminer/include/functions.inc.php:324 (cookie_path), :441-451 (redirect)
  • adminer/db.inc.php:52, adminer/edit.inc.php:20

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
None
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N

CVE ID

No known CVE

Weaknesses

Improper Input Validation

The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. Learn more on MITRE.

Unintended Proxy or Intermediary ('Confused Deputy')

The product receives a request, message, or directive from an upstream component, but the product does not sufficiently preserve the original source of the request before forwarding the request to an external actor that is outside of the product's control sphere. This causes the product to appear to be the source of the request, leading it to act as a proxy or other intermediary between the upstream component and the external actor. Learn more on MITRE.

URL Redirection to Untrusted Site ('Open Redirect')

The web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a redirect. Learn more on MITRE.

Credits