Current behavior
validateRedirectAsync verifies a signature only when the incoming message carries a
Signature query parameter. When it does not, hasValidSignatureForRedirect returns
without checking anything, and the message is processed:
if (container.Signature) {
// ... verify, throw "Invalid query signature" on failure
} else {
return true;
}
The Redirect binding makes signing optional at the binding level, which is where this
comes from. But nothing else in the path makes up for it. verifyLogoutRequest checks the
issuer and the timestamps, and both of those are read out of the same unsigned message, so
neither constrains anything. idpIssuer does not help for the same reason.
The result is that for an unsigned message the library reports success on content it has
not authenticated, which is the opposite of what AGENTS.md asks of it:
Trust only the bytes that were signed [...] A function that answers "did this
verify?" with a boolean cannot uphold this rule, because its caller still has to go find
the content somewhere else.
That shape is present here too: hasValidSignatureForRedirect returns boolean | void,
and validateRedirectAsync discards the return value entirely, relying on a throw. A
false return would be silently ignored.
Proposed change for v6
Require a valid signature on redirect-binding logout messages, and reject those without
one. This is breaking for anyone whose IdP sends unsigned logout messages, which is why it
is scheduled for a major rather than done now.
While changing it, hasValidSignatureForRedirect should stop returning a boolean and
either return normally or throw, so the result cannot be ignored.
Migration already in place
Non-breaking groundwork landed for 5.2.0 in #420:
- A
util.debuglog("node-saml") warning fires whenever an unsigned message is accepted,
naming the message type and stating that a future major will reject it.
test/tests.spec.ts pins the current accept-an-unsigned-message behavior under a name
that says it is pending rejection, so making this change here will fail that test and
force it to be rewritten deliberately rather than silently.
README.md documents the current behavior and this plan.
Checklist for this issue
Current behavior
validateRedirectAsyncverifies a signature only when the incoming message carries aSignaturequery parameter. When it does not,hasValidSignatureForRedirectreturnswithout checking anything, and the message is processed:
The
Redirectbinding makes signing optional at the binding level, which is where thiscomes from. But nothing else in the path makes up for it.
verifyLogoutRequestchecks theissuer and the timestamps, and both of those are read out of the same unsigned message, so
neither constrains anything.
idpIssuerdoes not help for the same reason.The result is that for an unsigned message the library reports success on content it has
not authenticated, which is the opposite of what
AGENTS.mdasks of it:That shape is present here too:
hasValidSignatureForRedirectreturnsboolean | void,and
validateRedirectAsyncdiscards the return value entirely, relying on a throw. Afalsereturn would be silently ignored.Proposed change for v6
Require a valid signature on redirect-binding logout messages, and reject those without
one. This is breaking for anyone whose IdP sends unsigned logout messages, which is why it
is scheduled for a major rather than done now.
While changing it,
hasValidSignatureForRedirectshould stop returning a boolean andeither return normally or throw, so the result cannot be ignored.
Migration already in place
Non-breaking groundwork landed for 5.2.0 in #420:
util.debuglog("node-saml")warning fires whenever an unsigned message is accepted,naming the message type and stating that a future major will reject it.
test/tests.spec.tspins the current accept-an-unsigned-message behavior under a namethat says it is pending rejection, so making this change here will fail that test and
force it to be rewritten deliberately rather than silently.
README.mddocuments the current behavior and this plan.Checklist for this issue
SignatureparameterhasValidSignatureForRedirectso its result cannot be discardedtest/tests.spec.tsto assert the rejectionREADME.mdand note the break in the release notes