php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81188 C14N wrong namespace order
Submitted: 2021-06-21 23:36 UTC Modified: 2021-06-27 00:41 UTC
From: bill dot seddon at lyquidity dot com Assigned:
Status: Closed Package: *XML functions
PHP Version: 8.0.7 OS: Windows 10
Private report: No CVE-ID: None
 [2021-06-21 23:36 UTC] bill dot seddon at lyquidity dot com
Description:
------------
My use case is checking XmlDSig signatures. The Xml in the test script is asnippet of the type found within a <transform> element of a signature.  The order of the canonicalized attributes and element nodes is important because the resulting Xml is hashed.  Unless the canonicalized Xml is generated correctly the hash will be different to a hash computed by other software.  Bear in mind that this existing Xml generated by other other application so the option to modify the input Xml does not exist. 

Note the order of the attributes in the <XPath> element: xmlns:dsig-xpath, filter, xmlns. 

When this is canonicalized using PHP 5.3 through to 8.0 using the test script provided the result is the Xml shown in the 'actual result'.

Here the order of the attributes is: xmlns:dsig-xpath, xmlns, filter

Correctly, the namespace attributes appear before value attributes.  However namespaces are being returned in their document order.

When using any other tool to canonicalize this fragment such as xmllint, MS Cryptography, Python lxml the namespace attributes are sorted by the attribute node name to give the result shown in the 'expected result'.

All other canonicalization implementations I can find order the attributes this way: xmlns, xmlns:dsig-xpath, filter.  I believe this is consistent with section 4.8 of the canonicalization specification: https://www.w3.org/TR/2001/REC-xml-c14n-20010315#SortByNSURI

Oddly, PHP, xmllint and Python's lxml are all based on libxml.  However it's the PHP implementation that yields a different result.


Test script:
---------------
$xml = "<XPath xmlns:dsig-xpath=\"http://www.w3.org/2002/06/xmldsig-filter2\" Filter=\"subtract\" xmlns=\"http://www.w3.org/2002/06/xmldsig-filter2\">some xpath</XPath>";

$doc = new \DOMDocument();
$doc->loadXML( $xml );
$doc->C14N( false, true );
$xml = $doc->saveXML( $doc->documentElement, LIBXML_NOEMPTYTAG );

Expected result:
----------------
<XPath xmlns="http://www.w3.org/2002/06/xmldsig-filter2" xmlns:dsig-xpath="http://www.w3.org/2002/06/xmldsig-filter2" Filter="subtract">some xpath</XPath>


Actual result:
--------------
<XPath xmlns:dsig-xpath="http://www.w3.org/2002/06/xmldsig-filter2" xmlns="http://www.w3.org/2002/06/xmldsig-filter2" Filter="subtract">some xpath</XPath>


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-06-22 17:17 UTC] bill dot seddon at lyquidity dot com
It may be this issue is that the C14N function of libxml exposed by the php_libxml extension only follows the canonical XML specification 1.0 from 2000 (https://www.w3.org/TR/2000/WD-xml-c14n-20000119.html) which explicitly avoids namespaces (see section 2.5).

In the meantime there is the more 'recent' (2008!) 1.1 specification which does cover namespaces in section 4.8.  Libxml does support C14N 1.1.  For example, xmllint includes the switch -c14n11 so this revision of the specification can be used.
 [2021-06-22 17:28 UTC] bill dot seddon at lyquidity dot com
The previous comment was not completely accurate.  In fact the need to sort namespaces was added to C14N 1.0 in 2001 (https://www.w3.org/TR/2001/REC-xml-c14n-20010315)
 [2021-06-27 00:41 UTC] bill dot seddon at lyquidity dot com
-Status: Open +Status: Closed
 [2021-06-27 00:41 UTC] bill dot seddon at lyquidity dot com
Unable to reproduce
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 20:01:30 2024 UTC