Summary
remote_addr() returns the value of the client-supplied X-Forwarded-For header whenever it is present, and this value is used for the ifpl_netmask_whitelist IP check that decides whether persistent ("keep me logged in") sessions are allowed. Because X-Forwarded-For is fully attacker-controlled, a client can spoof it to satisfy (or evade) the netmask whitelist.
- Type: Access-control decision based on a spoofable request header (CWE-290 / CWE-348)
- Reviewed at commit:
bde7b68 (v5.2.0, current master)
- Severity: Medium — bypasses the IP restriction intended to confine persistent logins to trusted networks (only relevant when
ifpl_netmask_whitelist is configured).
Root cause
remote_addr() trusts X-Forwarded-For unconditionally:
function remote_addr()
{
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
return $_SERVER["HTTP_X_FORWARDED_FOR"];
} else if (isset($_SERVER["REMOTE_ADDR"])) {
return $_SERVER["REMOTE_ADDR"];
}
return "";
}
and the returned value drives the whitelist check in persistent_login.php:49-54:
$netmaskwl = $rcmail->config->get('ifpl_netmask_whitelist', array());
if (!empty($netmaskwl)) {
$user_ip = $this->remote_addr();
...
if ($this->ip_in_range($user_ip, $netmaskwl[$i])) { $found = true; }
}
Any client can send X-Forwarded-For: <an IP inside the whitelist> to appear to originate from the trusted range, regardless of their real address. (Even without a reverse proxy in front of Roundcube, HTTP_X_FORWARDED_FOR is set purely from the request.)
Recommended fix
Do not trust X-Forwarded-For unconditionally. Roundcube core already solves this with rcube_utils::remote_addr(), which only honors forwarding headers when the request comes from a configured trusted proxy (proxy_whitelist). The plugin should delegate to rcube_utils::remote_addr() instead of reimplementing an unconditional-trust version, so the whitelist check runs against the real client address (or a proxy-provided one only when the proxy is trusted).
Disclosure
The repository has no SECURITY.md and GitHub private vulnerability reporting is not enabled, so there is no private channel; reporting here so it can be fixed. Happy to open a PR. Found during a security review of Roundcube and commonly-used plugins.
Summary
remote_addr()returns the value of the client-suppliedX-Forwarded-Forheader whenever it is present, and this value is used for theifpl_netmask_whitelistIP check that decides whether persistent ("keep me logged in") sessions are allowed. BecauseX-Forwarded-Foris fully attacker-controlled, a client can spoof it to satisfy (or evade) the netmask whitelist.bde7b68(v5.2.0, currentmaster)ifpl_netmask_whitelistis configured).Root cause
remote_addr()trustsX-Forwarded-Forunconditionally:and the returned value drives the whitelist check in
persistent_login.php:49-54:Any client can send
X-Forwarded-For: <an IP inside the whitelist>to appear to originate from the trusted range, regardless of their real address. (Even without a reverse proxy in front of Roundcube,HTTP_X_FORWARDED_FORis set purely from the request.)Recommended fix
Do not trust
X-Forwarded-Forunconditionally. Roundcube core already solves this withrcube_utils::remote_addr(), which only honors forwarding headers when the request comes from a configured trusted proxy (proxy_whitelist). The plugin should delegate torcube_utils::remote_addr()instead of reimplementing an unconditional-trust version, so the whitelist check runs against the real client address (or a proxy-provided one only when the proxy is trusted).Disclosure
The repository has no
SECURITY.mdand GitHub private vulnerability reporting is not enabled, so there is no private channel; reporting here so it can be fixed. Happy to open a PR. Found during a security review of Roundcube and commonly-used plugins.