where samlp:Response tells us that this is an element under the samlp namespace and then the xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" part tells us that samlp namespace is defined as "urn:oasis:names:tc:SAML:2.0:assertion". We can then look up this URI ( "urn:oasis:names:tc:SAML:2.0:assertion" ) and figure out what this Response element is and how we can validate it's syntactic correctness.
When you have XML like
<a>
<b>
</b>
</a>
you can either put all the namespace declarations on the root ( <a> in this case ) , or put each declaration where it is used, so some in <a> that are used by <a> and some by <b> that are used by <b>. The important thing is that the piece of software that needs to do the validation of the XML syntax, needs to know all , one way or another.
We detach this Encrypted Assertion and attempt to decrypt it on its own.
After we decrypt the EncryptedAssertion into an Assertion, we parse it and validate it syntactically before we use it. This happens automatically by virtue of org.opensaml.xmlsec.encryption.support.Decrypter#parseInputStream being called.
The manifestation of the issue
Certain IDP implementations add namespace declarations only on the root. For instance, a SAML Response can look like
<?xml version="1.0"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_e1ef86d712147fb585a3eb22c50a30270f080d2074" InResponseTo="_b5e9bfef6f2007d4571322283fff12bbf3d90abb" IssueInstant="2022-01-25T15:48:04Z" Destination="THESPACSHERE">
///// MORE SAML HERE - REMOVED FOR CLARITY
<saml:Assertion Version="2.0" ID="_929460c61aa2d7c1c2492c45c79da98a7cc004188a" IssueInstant="2022-01-25T15:48:04Z">
</saml:Assertion>
</samlp:Response>
The enclosed element is an <saml:Assertion> one but the saml namespace is defined in the root of the XML Document (xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion")
The IDP then encrypts this and sends it over as
<?xml version="1.0"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_e1ef86d712147fb585a3eb22c50a30270f080d2074" InResponseTo="_b5e9bfef6f2007d4571322283fff12bbf3d90abb" IssueInstant="2022-01-25T15:48:04Z" Destination="THESPACSHERE">
///// MORE SAML HERE - REMOVED FOR CLARITY
<saml:EncryptedAssertion>
</saml:EncryptedAssertion>
</samlp:Response>
Again, the namespace for <saml:EncryptedAssertion>, which is saml: , is defined only in the Response element.
When, during step 3 above, we decrypt that SAML message, we decrypt the Assertion element on its own and get :
During step 4 above, we (well, opensaml) try to parse and validate this and we fail ! We fail, because we see <saml:Assertion> on this element, but we have no idea about the saml namespace, as it is not defined inline. Example of an error:
Caused by: org.xml.sax.SAXParseException: Le préfixe "saml" de l'élément "saml:Assertion" n'est pas lié.
Under normal logging configuration this is partially masked and reported only as a Decryption error, apart from a single
[2022-01-25T16:48:07,058][ERROR][n.s.u.j.s.x.BasicParserPool] [node1] XML Parsing Error
message. Additional insights, which point to the exact issue at hand can be gained by setting
Elasticsearch Version
Elasticsearch fails to decrypt an encrypted SAML Assertion when it is missing the
Assertion
namespace declaration.It's not the decryption itself that fails but rather that we fail to parse the XML document after decrypting it.
Background
XML has namespaces. For instance SAML messages often start with
where
samlp:Response
tells us that this is an element under thesamlp
namespace and then thexmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
part tells us thatsamlp
namespace is defined as "urn:oasis:names:tc:SAML:2.0:assertion". We can then look up this URI ("urn:oasis:names:tc:SAML:2.0:assertion"
) and figure out what thisResponse
element is and how we can validate it's syntactic correctness.When you have XML like
you can either put all the namespace declarations on the root (
<a>
in this case ) , or put each declaration where it is used, so some in<a>
that are used by<a>
and some by<b>
that are used by<b>
. The important thing is that the piece of software that needs to do the validation of the XML syntax, needs to know all , one way or another.See: https://www.w3.org/TR/1999/REC-xml-names-19990114/#ns-decl for more details.
Elasticsearch's behavior when decrypting SAML messages
EncryptedAssertion
into an Assertion, we parse it and validate it syntactically before we use it. This happens automatically by virtue oforg.opensaml.xmlsec.encryption.support.Decrypter#parseInputStream
being called.The manifestation of the issue
Certain IDP implementations add namespace declarations only on the root. For instance, a SAML Response can look like
The enclosed element is an
<saml:Assertion>
one but thesaml
namespace is defined in the root of the XML Document (xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
) The IDP then encrypts this and sends it over asAgain, the namespace for
<saml:EncryptedAssertion>
, which issaml:
, is defined only in the Response element.When, during step 3 above, we decrypt that SAML message, we decrypt the Assertion element on its own and get :
<saml:Assertion>
on this element, but we have no idea about thesaml
namespace, as it is not defined inline. Example of an error:Under normal logging configuration this is partially masked and reported only as a Decryption error, apart from a single
message. Additional insights, which point to the exact issue at hand can be gained by setting
We should be able to consume such SAML Responses, AFAICT these are valid and there are a number of SP implementations that can handle them correctly.