Skip to content

SamlBase.register_prefix pollutes ElementTree namespace registry and causes AuthnResponse signature verification failure #981

@GMMan

Description

@GMMan

This problem is two-fold:

  • SamlBase.register_prefix registers namespace prefixes but nothing restores the registry to the previous state, so whatever prefixes that get registered remain registered for the process's lifetime.
  • Verification of SAML messages deserializes the incoming XML into a class, then reserializes it before passing it in for signature validation with the security context. Due to the prefix pollution, the XML changes slightly and ends up failing validation. Note that in my case it's validating Responses, and the assertions are signed then encrypted, while the outer samlp elements are not signed.
    • Subproblem, upon further research: it actually doesn't matter whether pollution is happening, ElementTree is dragging all namespaces it can find to the root element. The only reason the response is validating without pollution is because the namespaces outside the encrypted data aren't coinciding with the namespaces inside since the decryption was done by xmlsec1 and not passed through ElementTree.

Example overview

Imagine a Django web application with multiple workers and the environment as indicated at the bottom of this issue.

  1. AuthnRequest is generated via djangosaml2
    • djangosaml2 provides some prefix mappings to make the generated request prettier. register_prefix is called here and pollutes the worker the request was generated on
  2. User is redirected to IdP and signs in
  3. User receives a Response and is redirected back to the application
  4. Response processing
    • If hitting an unpolluted worker: XML passed to xmlsec1 starts with <ns0:Response ... and contains contains all namespace prefixes; however, it validates successfully
    • If hitting a polluted worker: XML passed to xmlsec1 starts with <samlp:Response ... and contains namespace prefixes for samlp, saml, ds, and xenc in the root element; it fails to validate

I'm kind of curious why the reserialized XML is validated instead of the original XML. I'm sure there are reasons for it, but it seems a bit counterintuitive and is subject to serialization shenanigans like this.

I also haven't wrapped my head around how XML canonicalization and signing works, so I can't offer any opinions on whether whatever other processing that has been done by pysaml2 is correct.

Environment

  • Python 3.11.11
  • Django 4.2.17
  • pysaml2 7.5.0 (modified)
  • djangosaml2 1.9.3 (plus some overrides)
  • xmlsec1 1.2.41
  • IdP uses ComponentSpace SAML

I'll check with my development team whether I can publicly attach examples of an original AuthnResponse, post-processed response without pollution, and post-processed response with pollution, and decrypted counterparts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions