Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue decrypting signed assertion under PHP 8.1 (7.4 is just fine) #562

Open
yphoenix opened this issue Jul 27, 2023 · 19 comments
Open

Issue decrypting signed assertion under PHP 8.1 (7.4 is just fine) #562

yphoenix opened this issue Jul 27, 2023 · 19 comments

Comments

@yphoenix
Copy link

yphoenix commented Jul 27, 2023

Starting with the response......

<ns5:Response
	xmlns:ns5="urn:oasis:names:tc:SAML:2.0:protocol"
	xmlns="http://www.w3.org/2009/xmlenc11#"
	xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion"
	xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"
	xmlns:ns4="http://www.w3.org/2001/04/xmlenc#" Destination="https://xxxx" ID="xxx" InResponseTo="ONELOGIN_81aeef9ca3d98b3fa3e505164baff00b3aeeab16" IssueInstant="2023-07-26T23:35:07.423Z" Version="2.0">
	<ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">xxx</ns2:Issuer>
	<ds:Signature
		xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
		<ds:SignedInfo>
			<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
			<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
			<ds:Reference URI="#_a13a2e99a1a23945f8d58c5df3f781772c47">
				<ds:Transforms>
					<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
					<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
				</ds:Transforms>
				<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
				<ds:DigestValue>xxxx=</ds:DigestValue>
			</ds:Reference>
		</ds:SignedInfo>
		<ds:SignatureValue>xxx==</ds:SignatureValue>
		<ds:KeyInfo
			xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
			<ds:X509Data>
				<ds:X509Certificate>MIIFxxxxx=</ds:X509Certificate>
			</ds:X509Data>
		</ds:KeyInfo>
	</ds:Signature>
	<ns5:Status>
		<ns5:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
	</ns5:Status>
	<ns2:EncryptedAssertion
		xmlns="http://www.w3.org/2009/xmlenc11#"
		xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion"
		xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"
		xmlns:ns4="http://www.w3.org/2001/04/xmlenc#"
		xmlns:ns5="urn:oasis:names:tc:SAML:2.0:protocol">
		<xenc:EncryptedData
			xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
			<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
			<ds:KeyInfo
				xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
				<xenc:EncryptedKey
					xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
					<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
					<xenc:CipherData>
						<xenc:CipherValue>xxxxx==</xenc:CipherValue>
					</xenc:CipherData>
				</xenc:EncryptedKey>
				<ds:X509Data>
					<ds:X509Certificate>MIIGMxxxxxx</ds:X509Certificate>
				</ds:X509Data>
			</ds:KeyInfo>
			<xenc:CipherData>
				<xenc:CipherValue>xxxxx</xenc:CipherValue>
			</xenc:CipherData>
		</xenc:EncryptedData>
	</ns2:EncryptedAssertion>
</ns5:Response>

It all gets decrypted just fine (8.1 & 7.4), ...

<ns2:Assertion xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_f1a2269c3902a05d627c7829ab8e3eed50bd" IssueInstant="2023-07-26T23:35:07.423Z" Version="2.0">
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">xxx</ns2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod><ds:Reference URI="#_f1a2269c3902a05d627c7829ab8e3eed50bd"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod><ds:DigestValue>xxx=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>xxx==</ds:SignatureValue><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIIF...=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>
        <ns2:Subject>
            <ns2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">1039893981</ns2:NameID>
            <ns2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <ns2:SubjectConfirmationData InResponseTo="ONELOGIN_81aeef9ca3d98b3fa3e505164baff00b3aeeab16" NotOnOrAfter="2023-07-26T23:36:37.407Z" Recipient="xxx"></ns2:SubjectConfirmationData>
            </ns2:SubjectConfirmation>
        </ns2:Subject>
        <ns2:Conditions NotBefore="2023-07-26T23:34:37.407Z" NotOnOrAfter="2023-07-26T23:36:37.407Z">
            <ns2:AudienceRestriction>
                <ns2:Audience>xxx</ns2:Audience>
            </ns2:AudienceRestriction>
        </ns2:Conditions>
        <ns2:AuthnStatement AuthnInstant="2023-07-26T23:34:32.401Z" SessionIndex="Qn7OdKYdWW5JzPPHtWEeGjGmM54=YbXNfA==">
            <ns2:AuthnContext>
                <ns2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</ns2:AuthnContextClassRef>
            </ns2:AuthnContext>
        </ns2:AuthnStatement>
        <ns2:AttributeStatement>
			...
        </ns2:AttributeStatement>
</ns2:Assertion>

In particular the ds:Signature section..

<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod><ds:Reference URI="#_f1a2269c3902a05d627c7829ab8e3eed50bd"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod><ds:DigestValue>xxx=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>xxx==</ds:SignatureValue><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIIF...=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>

Now all goes well until the decrypted XML is merged in to replace the encrypted XML in the Utils::treeCopyReplace() routine
After which it becomes...
PHP 7.4

<ns2:Assertion xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_f1a2269c3902a05d627c7829ab8e3eed50bd" IssueInstant="2023-07-26T23:35:07.423Z" Version="2.0">
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">xxx</ns2:Issuer><ds:Signature><ds:SignedInfo><ds:CanonicalizationMethod...

All OK, but PHP 8.1

<ns2:Assertion xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_f1a2269c3902a05d627c7829ab8e3eed50bd" IssueInstant="2023-07-26T23:35:07.423Z" Version="2.0">
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">xxx</ns2:Issuer><ns3:Signature><ns3:SignedInfo><ds:CanonicalizationMethod ...

The <ds:...> blocks become <ns3:...> blocks and of course the signature then fails.... because the data that was signed has been mangled and become something else.

Note: PHP 7.4, perfect, 8.1, not so much.

Thanks

@yphoenix
Copy link
Author

yphoenix commented Jul 27, 2023

Seems to be $targetNode->appendChild($clonedNode)

$sourceNode: <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_f1a2269c3902a05d627c7829ab8e3eed50bd"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>xxx=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>xxx</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature>

$clonedNode => <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>

$targetNode => <ns2:Assertion xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_f1a2269c3902a05d627c7829ab8e3eed50bd" IssueInstant="2023-07-26T23:35:07.423Z" Version="2.0">
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">xxx</ns2:Issuer></ns2:Assertion>

$targetNode->appendChild($clonedNode)

$resultNode => <ns3:Signature/>

On PHP 7.4 it's <ds:Signature/>

@yphoenix
Copy link
Author

Saving and restoring the namespace prefix works around the issue as there doesn't seem to be a way to stop PHP from changing the namespace on you....

    public static function treeCopyReplace(DomNode $targetNode, DomNode $sourceNode, $recurse = false)
    {
        if ($targetNode->parentNode === null) {
            throw new Exception('Illegal argument targetNode. It has no parentNode.');
        }
        $clonedNode = $targetNode->ownerDocument->importNode($sourceNode, false);

        $ns = $clonedNode->prefix;             // SAVE

        if ($recurse) {
            $resultNode = $targetNode->appendChild($clonedNode);
        } else {
            $resultNode = $targetNode->parentNode->insertBefore($clonedNode, $targetNode);
        }

        if ($resultNode->prefix !== $ns)
        {
            $resultNode->prefix = $ns;        // RESTORE
        }

@yphoenix
Copy link
Author

For splicing in the decrypted XML does this make sense? Surely it should be as decrypted and not manipulated!

@pitbulk
Copy link
Contributor

pitbulk commented Aug 11, 2023

@yphoenix thanks for your research, I will investigate and merge your PR if makes sense.

@Navi2016
Copy link

Navi2016 commented Sep 14, 2023

I might be running into the same issue after updating my server from PHP 8.2.7 to 8.2.8.

Signatures are no longer valid and SAML login fails.

It's in a Laravel 10 app using 24slides/laravel-saml2 (2.2.0), which uses onelogin/php-saml (4.1.0).

Everything works great on PHP 8.2.7 and before, no longer on 8.2.8 and up.

As someone mentioned before, there are quite some DOM patches in 8.2.8.

When using the fixed version (php-saml-4.1.0-Issio-Fix-10609) it does not work yet, getting a different error though:
DOMDocument::loadXML(): Failed to parse QName ':Issuer' in Entity, line: 6

@Navi2016
Copy link

Navi2016 commented Sep 14, 2023

Some additional info, the exact errors i'm getting are:
invalid_response from vendor/onelogin/php-saml/src/Saml2/Auth.php:252

and

Reference validation failed from vendor/robrichards/xmlseclibs/src/XMLSecurityDSig.php:614 (v 3.1.1)

@yphoenix
Copy link
Author

Try the fix I proposed above to see if it fixes things for you. I only had issues with encrypted assertions. When it was decrypted the XML tree would get messed up when treeCopyReplace() replaced the encrypted node with the decrypted version.

@Navi2016
Copy link

Like i mentioned i have tried the fixed version (from the merge), but it did not resolve the issue :(

@yphoenix
Copy link
Author

In that case probably something different. Took me a good 18 hours of stepping through the code running it both under 7.4 and 8.1 to find the difference and thus my fix to my problem. Looks like your issue might be in the xmlseclibs library.

@nielsdos
Copy link

PHP DOM maintainer here.
This is caused by a regression introduced by a bugfix in PHP. This is a behaviour change that's caused by an unintended side-effect of using a newer namespace API from libxml2.
I have opened a PR to fix this at PHP's end by restoring the original behaviour.
I have run these tests https://github.com/SAML-Toolkits/php-saml/actions/runs/6269430086/job/17025850045 to verify if the behaviour is OK now, and it seems to pass the XML related tests.

@pitbulk
Copy link
Contributor

pitbulk commented Sep 27, 2023

@nielsdos , thanks for the update!

nielsdos added a commit to php/php-src that referenced this issue Sep 27, 2023
The xmlDOMWrapReconcileNamespaces method we used to fix the namespace
corruption issues in 8.1.21/8.2.8 caused regressions.
Primarily, there is a similar corruption that the xmlReconciliateNs method
used to have in which a namespace is suddenly shifted
(SAML-Toolkits/php-saml#562) and the side-effect of removing redundant
namespaces causes problems when a specific serialization is required.

Closes GH-12308.
@yphoenix
Copy link
Author

Thanks @nielsdos - looking forward to not needing the patch anymore.

@yphoenix
Copy link
Author

So this looks like it will be in the next release of PHP 8.x, missed the release that was put out today (2023-09-28)

@pitbulk
Copy link
Contributor

pitbulk commented Sep 29, 2023

@yphoenix, should we detect at the code affected PHP versions and apply your patch only on those cases?
Do you want then to adapt your PR and add test cases?

@yphoenix
Copy link
Author

That is an interesting idea, however, first I need to check that it is indeed fixed in 8.1.25 and then decided whether to work on a fix that is needed for PHP 8.1.21-8.1.24 (and appropriate 8.2 / 8.3 versions) or suggest that it is documented that they are broken and leave it at that.

@Navi2016
Copy link

Navi2016 commented Nov 1, 2023

Issue seems to be resolved in PHP 8.2.12!

@yphoenix
Copy link
Author

yphoenix commented Nov 1, 2023

Suspect it is also fixed in 8.1.25 but haven’t tested it yet.

@pitbulk
Copy link
Contributor

pitbulk commented Dec 29, 2023

@yphoenix, do you thinhk you gonna have time to review the work to be done here?

@nielsdos
Copy link

FWIW, this is indeed also fixed in 8.1.25, 8.2.12, 8.3+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants