CVE-2026-49284: ExpectedIssuer and InResponseTo binding bypass in SimpleSAMLphp
I reported a SAML SP state-binding issue in SimpleSAMLphp that was published as CVE-2026-49284 / GHSA-q8r6-xj3f-wrrm.
The issue affected SP-initiated login flows in multi-IdP deployments. If SimpleSAMLphp created state for one expected IdP, but later received a valid response from a different trusted IdP, the ACS path logged a warning and continued processing instead of rejecting the response.
That issuer mismatch became security-relevant when combined with an unsigned response-level InResponseTo and a signed assertion that did not carry its own SubjectConfirmationData/InResponseTo.
Advisory
- CVE:
CVE-2026-49284 - GitHub Advisory:
GHSA-q8r6-xj3f-wrrm - Package:
simplesamlphp - Affected version listed in advisory:
2.5.0 - Patched versions listed in advisory: none
- Severity: High 7.1/10
- CVSS:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N - CWE:
CWE-345: Insufficient Verification of Data Authenticity
Root Cause
In an SP-initiated SAML login, the SP chooses an IdP and sends an AuthnRequest. SimpleSAMLphp stored that selected IdP in state as ExpectedIssuer.
The expected security property is straightforward:
If the SP created login state for IdP A, then a response from IdP B should not satisfy that state.
The vulnerable behavior broke that property. When the ACS received a response from an issuer different from ExpectedIssuer, the code logged a warning and continued into response processing.
The second part of the issue was request correlation:
- the outer
Response/@InResponseTocan point to the SP state; - the signed assertion may omit
SubjectConfirmationData/@InResponseTo; - the code accepted the response as long as it did not see conflicting values.
That meant an unsigned response-level InResponseTo could bind the message to SP-initiated state, while the signed assertion itself did not cryptographically carry the same request binding.
Attack Shape
The practical scenario is a multi-IdP SP deployment where IdPs are not interchangeable security principals.
Example:
- The SP starts a login flow for IdP A.
- The saved state records
ExpectedIssuer = IdP A. - The attacker obtains a valid signed assertion from IdP B.
- The outer SAML Response contains
InResponseTofor the SP state created for IdP A. - The signed assertion does not include
SubjectConfirmationData/InResponseTo. - The ACS accepts the assertion from IdP B while completing state created for IdP A.
This is not signature forgery. IdP B’s assertion can be validly signed as IdP B. The bug is that the SP used unsigned response-level request correlation to satisfy state that was intentionally created for a different IdP.
Impact
The impact depends heavily on the deployment.
The issue matters most when:
- the SP trusts multiple IdPs;
- those IdPs represent different tenants, assurance levels, or attribute namespaces;
- the application gives security meaning to the selected or expected IdP;
- identifiers or attributes from different IdPs can collide or be interpreted globally;
enable_unsolicitedis used as part of the deployment’s defense against IdP-initiated login.
In those deployments, a lower-trust IdP can satisfy SP state created for a different expected IdP. That can bypass IdP selection, tenant routing, or assurance-level assumptions.
Validation Matrix
During triage, I used a small matrix to separate the current behavior from the intended behavior.
The vulnerable behavior accepted these cases:
1
2
3
4
5
solicited_outer_only ACCEPT
solicited_both_match ACCEPT
solicited_both_mismatch REJECT
unsolicited_no_inresponseto ACCEPT
unsolicited_scd_only ACCEPT
The important rows were:
solicited_outer_only:Response/InResponseTois present, but signedSubjectConfirmationData/InResponseTois missing.unsolicited_scd_only: response-levelInResponseTois absent, but assertion-levelSubjectConfirmationData/InResponseTois present.
The intended fix direction was:
- reject
ExpectedIssuermismatches in SP-initiated flows; - for solicited responses, require both
Response/InResponseToandSubjectConfirmationData/InResponseToto be present and to match the AuthnRequest ID; - for unsolicited responses, reject if either
InResponseTovalue is present.
Why This Was Subtle
The vulnerable flow was not a single missing if around a signature check.
It combined:
- state created for one selected IdP;
- response processing for a different trusted IdP;
- acceptance of assertion-only signatures;
- request correlation based on unsigned response-level data;
- missing rejection when assertion-level
SubjectConfirmationData/InResponseTowas absent.
Each piece can appear compatible with existing SAML deployments in isolation. The security issue appears when the SP treats those pieces together as enough to complete an SP-initiated flow for a specific expected IdP.
References
- GitHub Advisory: https://github.com/simplesamlphp/simplesamlphp/security/advisories/GHSA-q8r6-xj3f-wrrm
- SimpleSAMLphp project: https://github.com/simplesamlphp/simplesamlphp