Skip to content

fix!: validate signatures against live message to prevent XSW#6

Merged
william-suppo merged 2 commits into
litesaml:masterfrom
theodorejb:fix-xsw
Jul 2, 2026
Merged

fix!: validate signatures against live message to prevent XSW#6
william-suppo merged 2 commits into
litesaml:masterfrom
theodorejb:fix-xsw

Conversation

@theodorejb

Copy link
Copy Markdown
Contributor

Resolves #5

POST-binding signature validation (added in 2.1.0, #4) is vulnerable to XML Signature Wrapping (XSW): an attacker holding one genuinely signed Response can wrap it so the valid signature covers a hidden copy while the application consumes a forged assertion, and handle*(validate: true) reports it as valid. I added a test for this, which fails on v2.1.0 but is fixed here.

The vulnerability

2.1.0 validates by flattening the message signature into a value/algorithm/data DTO and verifying that. For an enveloped POST signature this reconstructs a detached SignatureStringReader and checks only that the signature covers SignedInfo — it never runs LightSAML's XSW defense (SignatureXmlReader::validate()assertNoXmlSignatureWrapping()), because a flattened DTO has no way to express the document structure that check inspects (reference targets, duplicate IDs). The hole is reachable both via handle*(validate: true) and the public validateSignature().

The fix

Validate the live LightSAML message instead of a flattened DTO, delegating to the signature reader's own validate()SignatureStringReader for Redirect, SignatureXmlReader for POST. SignatureXmlReader::validate() runs the wrapping check that a flattened representation cannot. As a result, validation now needs the live message (not the DTO).

Breaking changes

  • validateSignature() now takes the live SamlMessage, not a message DTO.
  • Removed extractSignature().
  • After-the-fact validation is removed. Signatures are verified inline via handle*(validate: true, issuer: ...), so the trusted issuer must be supplied when handling the message rather than read from the returned DTO and validated afterward.
  • The Signature class and the signature property on message DTOs are gone. Message DTOs are now pure parsed data.

theodorejb and others added 2 commits July 2, 2026 11:20
Signature validation flattened the message signature into a value/algorithm/data DTO and verified that. For HTTP-POST (enveloped) signatures this reconstructed a detached SignatureStringReader and checked only that the signature covered SignedInfo - dropping LightSAML's XML Signature Wrapping defense. An attacker could wrap a genuinely signed Response so the valid signature covered a hidden copy while a forged assertion was consumed, and it validated. The flaw was reachable both via handle*(validate: true) and the public validateSignature() method.

Validate the live SamlMessage instead, delegating to the signature reader's own validate(), which runs the wrapping check that a flattened DTO cannot express. extractSignature() no longer flattens enveloped XML signatures, so validateSignature() no longer reports a wrapped POST message as valid.

Resolves litesaml#5
Validation was split in two: validateMessageSignature(SamlMessage) for the
live message (used by handle*(validate: true)) and validateSignature(Message)
for after-the-fact validation from the flattened DTO. The DTO-based path only
worked for detached Redirect signatures — for POST it could never run the XML
Signature Wrapping check, which needs the live document.

Keep only the live-message method, renamed to validateSignature(SamlMessage).
Drop the public DTO-based validateSignature(), extractSignature(), the Signature
DTO, and the now-unused signature field on message DTOs. Signatures are verified
inline during handle*(validate: true); the message DTOs are pure parsed data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@william-suppo william-suppo changed the title Validate signatures against live message to prevent XSW fix!: validate signatures against live message to prevent XSW Jul 2, 2026
@william-suppo william-suppo merged commit 1a5ed8e into litesaml:master Jul 2, 2026
3 checks passed
@theodorejb theodorejb deleted the fix-xsw branch July 3, 2026 01:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XML Signature Wrapping: forged assertions accepted for POST-binding responses

2 participants