github.com/moov-io/signedxml: Moov signedxml: Unauthenticated denial of service in exclusive XML canonicalization
What fixes it
- 1.4.1github.com/moov-io/signedxml, goGitHub
go get github.com/moov-io/signedxml@v1.4.1 - 1.4.1github.com/moov-io/signedxml, GoOSV
go get github.com/moov-io/signedxml@v1.4.1
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Exclusive XML canonicalization (http://www.w3.org/2001/10/xml-exc-c14n#) in github.com/moov-io/signedxml copied the entire accumulated namespace map at every node during ValidateReferences(), before any reference digest or signature check.
For a chain of nested elements that each declare many unused namespace prefixes, that produced O(m × depth²) time and peak memory (m = prefixes per level). beevik/etree caps tree depth (~1024), but m is unbounded, so a small attacker-controlled document (on the order of 1 MB) could drive multi-gigabyte allocations and OOM the process. No valid signature, matching certificate, or authentication is required.
This is the library’s core path for SAML / WS-Federation service providers, which canonicalize attacker-supplied XML prior to authentication.
Affected
- Package:
github.com/moov-io/signedxml - Function:
ExclusiveCanonicalization(exclusivecanonicalization.go) - Verified: v1.4.0 and current default branch (commit
d79064a). Earlier v1.2.x–v1.3.x releases are likely affected.
Remediation
Do not copy the whole namespace map per node. Maintain a single scope and push/pop only the prefixes introduced at each element. Replace linear prefix-in-scope scans with set lookups. Impose a defensive cap on in-scope namespace declarations and return an error rather than allocating unbounded memory.
From @shuji-oh
Thanks for the fix — I re-ran my PoC against this branch and confirmed the memory blowup is gone. Before (main) -- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) -- depth=250 m=10 payload= 0.07MB allocated= 45MB depth=500 m=10 payload= 0.14MB allocated= 179MB depth=1000 m=10 payload= 0.27MB allocated= 711MB -- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m -- depth=1000 m=5 payload= 0.16MB allocated= 377MB depth=1000 m=100 payload= 2.57MB allocated= 6278MB After (fix/exc-c14n-namespace-dos) -- Doubling depth each time (m=10 fixed): allocated grows ~4x each time ≈ O(depth^2) -- depth=250 m=10 payload= 0.07MB allocated= 2MB depth=500 m=10 payload= 0.14MB allocated= 4MB depth=1000 m=10 payload= 0.27MB allocated= 7MB -- Increasing m (depth=1000 fixed): allocated scales roughly linearly with m -- depth=1000 m=5 payload= 0.16MB allocated= 4MB depth=1000 m=100 payload= 2.57MB allocated= 47MB That's roughly a 100–130x reduction in peak allocation for the same payload sizes, and no OOM in any of my runs. This looks like it resolves the issue — thanks for the quick fix!
Credit
Reported by Shuji Ohira (@shuji-oh).
Text from GitHub Advisory Database
Affected versions 3
- github.com/moov-io/signedxml, go<= 1.4.0GitHub
- signedxmlaffected: < 1.4.1CVE List
- github.com/moov-io/signedxml, Gofrom first release, fixed in 1.4.1OSV
References 5
- github.com/moov-io/signedxml/commit/da2cd6126c4c53b7e59a367d287786e6f6a2d0d3Fixvia CVE List
- github.com/moov-io/signedxml/releases/tag/v1.4.1Fixvia CVE List
- github.com/moov-io/signedxml/security/advisories/GHSA-5588-rp99-5fjhAdvisoryvia CVE List
- github.com/advisories/GHSA-5588-rp99-5fjhAdvisoryvia GitHub
- nvd.nist.gov/vuln/detail/CVE-2026-113729Referencevia GitHub