mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
2eec0549a3
/api/saml/{id}/slo cleared the cookie-selected session for any
LogoutResponse the IdP had signed in the last 90 seconds. crewjam/saml
v0.5.1 checks a LogoutResponse's signature, Destination, IssueInstant,
Issuer and Status but never its InResponseTo, and the route must take
GET for the HTTP-Redirect binding, where the SameSite=Lax session cookie
rides a cross-site navigation and the CSRF check does not apply. A user
who logged out through Pulse could replay the IdP's answer to their own
logout through a link and log out anyone who followed it. Delivered as a
cross-site HTTP-POST form instead, the response arrived without the
victim's cookie, yet the handler's deletion cookies still logged their
browser out.
SP-initiated logout now records each LogoutRequest ID on the provider's
service for up to ten minutes, bound to the session-store hash of the
session it ends. The SLO handler spends the record the response's
InResponseTo names and refuses the response unless that record exists
and is unexpired and the browser carries either no session or the
session that logout ended. The genuine return carries no session,
because logout clears the cookie before redirecting to the IdP, so
requiring the cookie-selected session to match would refuse every real
SLO. Refusals answer 403 with a failed saml_slo_callback audit event,
and a misdelivered response still spends its record, so each
LogoutRequest is answered at most once.
The SLO handler no longer writes session state or cookies for an
accepted response: logout already invalidated the session and expired
its cookies, and the callback cannot see which session a cookieless POST
delivery's browser holds. InResponseTo is read only from the root
element's unprefixed attribute, because encoding/xml would also fill it
from a namespace declaration such as xmlns:InResponseTo, which exclusive
canonicalization can leave outside the signature when nothing uses it.
SP-initiated logout itself now answers 401 to a request without a
session cookie. The SAML routes are public and skip CSRF, so a
cookieless cross-site POST fell through to the plain logout and its
deletion cookies, a forced logout that a deployment with authentication
configured refuses on /api/logout as unauthenticated.