gnodet-bot commented on code in PR #26726:
URL: https://github.com/apache/camel/pull/26726#discussion_r4080616337


##########
components/camel-xmlsecurity/src/main/java/org/apache/camel/component/xmlsecurity/api/DefaultXmlSignature2Message.java:
##########
@@ -155,7 +196,11 @@ public void mapToMessage(Input input, Message output) 
throws Exception {
                 node = getNodeForMessageBodyInEnvelopingCase(input);
             } else {
                 // enveloped or detached XML signature  --> remove signature 
element
-                node = input.getMessageBodyDocument().getDocumentElement();
+                Element documentElement = 
input.getMessageBodyDocument().getDocumentElement();
+                if (enforceReferenceCoverage) {
+                    checkDocumentElementIsCoveredByAReference(input, 
documentElement);

Review Comment:
   ๐ŸŸ  **Integration path through `mapToMessage` is still not covered by any 
test.**
   
   `DefaultXmlSignature2MessageReferenceCoverageTest` calls 
`checkDocumentElementIsCoveredByAReference` directly with a stub 
`Input`/`Reference`, so the `if (enforceReferenceCoverage)` guard at this line 
and the full `mapToMessage` โ†’ verify endpoint path are not exercised at all. A 
single test in `XmlSignatureTest` that:
   1. Signs a fragment with an id-based reference
   2. Wraps the signed fragment inside a larger document
   3. Validates the wrapped document with `enforceReferenceCoverage=true` set 
on a `DefaultXmlSignature2Message` bean
   4. Asserts a `XmlSignatureException` is thrown
   
   would be the proof that the feature works end-to-end. Without it, a 
regression that breaks the wiring (e.g. the `enforceReferenceCoverage` field is 
checked but the exception propagation path fails) would go undetected. This is 
the gap `davsclaus` raised and it was not addressed by the unit test additions.



##########
components/camel-xmlsecurity/src/main/java/org/apache/camel/component/xmlsecurity/api/DefaultXmlSignature2Message.java:
##########
@@ -314,6 +359,110 @@ protected Node 
getNodeForMessageBodyInEnvelopingCase(Input input) throws Excepti
         return node;
     }
 
+    /**
+     * Checks that a validated Reference actually covered the document element 
the default search is about to emit.
+     * <p>
+     * Core signature validation only proves that each Reference's digest 
matches the content that Reference resolves
+     * to. It says nothing about the rest of the document. So an attacker can 
take a legitimately signed fragment, embed
+     * it unchanged inside a larger document of their own, and validation 
still passes - the same-document URI resolves
+     * to that fragment exactly as before - while this method would hand the 
whole attacker document downstream as
+     * verified content. That is XML signature wrapping.
+     * <p>
+     * The check is deliberately narrow, so that it rejects that shape and 
nothing else. It only complains when the
+     * signature carries same-document references and none of them covers the 
document element. A Reference with an
+     * empty URI covers the whole document, and a signature whose References 
are all external says nothing about this
+     * document either way, so both are left alone.
+     *
+     * @param input           the verification input, carrying the validated 
References
+     * @param documentElement the element the default search would emit
+     */
+    protected void checkDocumentElementIsCoveredByAReference(Input input, 
Element documentElement) throws Exception {
+        List<Reference> references = getReferencesForMessageMapping(input);
+        if (references == null || references.isEmpty()) {
+            return;
+        }
+
+        boolean sameDocumentReferenceSeen = false;
+        for (Reference reference : references) {
+            String uri = reference.getURI();
+            if (uri == null) {
+                // An absent URI tells us nothing about this document. Like an 
external reference below it must not
+                // short-circuit the check for the references that follow it; 
a lone absent-URI reference still leaves
+                // sameDocumentReferenceSeen false, so the document is 
correctly rejected.
+                continue;

Review Comment:
   ๐ŸŸก **Design choice is valid but the comment misrepresents the spec โ€” worth a 
one-line fix.**
   
   Per the `javax.xml.crypto.URIReference.getURI()` contract, `null` means the 
`URI` attribute is **absent** from the `<Reference>` element. Per the W3C XML 
Signature spec (ยง4.3.3.2), an absent URI identifies the whole enclosing 
document โ€” identical in effect to `URI=""`. So a `null` URI that survived 
validation **does** cover the document element; treating it as `continue` is 
conservative (safe, never fail-open) but it will produce a false rejection for 
any conforming signature that omits the `URI` attribute entirely.
   
   oscerd's reasoning โ€” *"treating absent URI as 'accept' would accept the 
wrapping case via a crafted absent-URI reference"* โ€” is not quite right: by the 
time this check runs, the signature processor has already **validated** the 
reference, so the absent-URI reference's digest was verified against the whole 
document content. An attacker cannot attach a null-URI reference to a signature 
that was originally signed over a fragment, because the digest would not match.
   
   The current behaviour (false-positive for null-URI signatures, no 
false-negatives) is the safer tradeoff, but the comment should say so:
   
   ```suggestion
               if (uri == null) {
                   // Absent URI (getURI() == null per JSR-105) identifies the 
whole document per the XML Signature
                   // spec, the same as URI="". However, treating it as 
whole-document coverage here would let an
                   // attacker bypass the check by attaching a null-URI 
reference, so we skip it conservatively:
                   // a lone absent-URI reference leaves 
sameDocumentReferenceSeen false and the document is rejected.
                   continue;
               }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to