EGroupware's SAML login handler passes the raw user-supplied idp request parameter directly to SimpleSAMLphp's requireAuth() after only checking that the value starts with https://. No validation is performed against a configured list of trusted Identity Provider entity IDs. An unauthenticated attacker can supply any HTTPS URL pointing to an IdP they control, redirecting the authentication flow to a malicious server.
Summary
| Field |
Value |
| Advisory |
GHSA-pr4x-6fj4-gm4h |
| Severity |
Medium |
| CWE |
CWE-601 (URL Redirection to Untrusted Site) |
| Package |
egroupware (PHP) |
| Affected |
< 26.5.20260507 |
| Vulnerability |
User-Controlled Identity Provider Selection |
| Impact |
Credential phishing, potential authentication bypass via forged SAML assertions |
The login() function in api/src/Auth/Saml.php accepts an idp parameter from the HTTP request and forwards it to SimpleSAMLphp without verifying that it corresponds to a trusted, pre-configured Identity Provider. The only check applied is a regex that confirms the value begins with https://, which is trivially satisfied by any attacker-controlled HTTPS URL. This gives an unauthenticated attacker full control over which IdP processes the authentication request.
How it works
The vulnerable code is contained in the login() function of api/src/Auth/Saml.php:
// api/src/Auth/Saml.php
function login()
{
// login (redirects to IdP)
$as = new SimpleSAML\Auth\Simple(self::$auth_source);
$as->requireAuth(preg_match('|^https://|', $_REQUEST['idp']) ?
['saml:idp' => $_REQUEST['idp']] : []);
The ternary expression evaluates whether $_REQUEST['idp'] starts with https://. If it does, the value is passed verbatim as the saml:idp option to requireAuth(), instructing SimpleSAMLphp to initiate authentication against that specific IdP. If it does not, an empty options array is passed and the default IdP is used.
The check provides no actual trust validation. It confirms only that the attacker typed https:// at the start of their URL. Any HTTPS endpoint, including one the attacker operates, satisfies the condition and will be used as the authentication target.
Root Cause
The application conflates URL format validation with trust validation. Checking that a string is a well-formed HTTPS URL says nothing about whether the endpoint it points to is a trusted party in the SAML federation. The correct control is an allowlist: the application should maintain a set of known, configured IdP entity IDs and reject any idp value that is not an exact match for one of them. The current implementation has no such list and performs no such check.
Prerequisites
- No authentication is required. The vulnerability is reachable from the login page before any session exists.
- The attacker needs a valid HTTPS endpoint they control that is capable of responding to SAML authentication requests.
- A victim user must visit the crafted login URL, making this a user-interaction-required attack, typical of phishing scenarios.
Fix
Validate the idp parameter against a pre-configured allowlist of trusted IdP entity IDs before passing it to requireAuth(). The trusted list should be sourced from the application's SAML configuration, not derived from the request:
// Before (vulnerable): only format is checked, any HTTPS URL is accepted
$as->requireAuth(preg_match('|^https://|', $_REQUEST['idp']) ?
['saml:idp' => $_REQUEST['idp']] : []);
// After (safe): idp must exactly match a configured, trusted entity ID
$trusted_idps = self::get_trusted_idps(); // load from SAML config, not from request
$requested_idp = $_REQUEST['idp'] ?? '';
if (!in_array($requested_idp, $trusted_idps, true))
{
// Log the attempt and fall back to the default IdP
error_log('SAML: untrusted idp parameter rejected: ' . $requested_idp);
$as->requireAuth([]);
}
else
{
$as->requireAuth(['saml:idp' => $requested_idp]);
}
The allowlist must use strict comparison (true as the third argument to in_array) and must be loaded from a server-side configuration source that the user cannot influence.
Key Takeaways
- Format validation is not trust validation. Confirming that a URL starts with
https:// only verifies that TLS is in use on whatever server the attacker chose. It says nothing about whether that server is a trusted federation participant.
- IdP selection must be server-side. Any application that supports multiple Identity Providers must maintain the list of permitted IdPs on the server and validate user input against it. The client must never be the authority on which IdP to use.
- The attack surface is the login page itself. Because the vulnerable parameter is processed before authentication, no credentials or session are required to exploit this, making it trivially accessible to any internet-facing attacker.
- SAML authentication bypass is a worst-case outcome. Depending on SimpleSAMLphp's metadata validation strictness, this vulnerability can escalate from phishing to full authentication bypass, granting the attacker arbitrary user impersonation including administrative access.