VYPR
Moderate severityNVD Advisory· Published Nov 18, 2015· Updated May 6, 2026

CVE-2015-5253

CVE-2015-5253

Description

The SAML Web SSO module in Apache CXF before 2.7.18, 3.0.x before 3.0.7, and 3.1.x before 3.1.3 allows remote authenticated users to bypass authentication via a crafted SAML response with a valid signed assertion, related to a "wrapping attack."

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.cxf:cxf-rt-rs-security-sso-samlMaven
< 2.7.182.7.18
org.apache.cxf:cxf-rt-rs-security-sso-samlMaven
>= 3.0.0, < 3.0.73.0.7
org.apache.cxf:cxf-rt-rs-security-sso-samlMaven
>= 3.1.0, < 3.1.33.1.3

Affected products

1
  • cpe:2.3:a:apache:cxf:*:*:*:*:*:*:*:*
    Range: <2.7.18

Patches

3
845eccb6484b

Adding SAML SSO tests.

https://github.com/apache/cxfColm O hEigeartaighJul 30, 2015via ghsa
3 files changed · +226 5
  • rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java+4 5 modified
    @@ -92,7 +92,7 @@ public SSOValidatorResponse validateSamlResponse(
             }
             
             // Validate Assertions
    -        boolean foundValidSubject = false;
    +        org.opensaml.saml.saml2.core.Assertion validAssertion = null;
             Date sessionNotOnOrAfter = null;
             for (org.opensaml.saml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
                 // Check the Issuer
    @@ -114,7 +114,7 @@ public SSOValidatorResponse validateSamlResponse(
                     org.opensaml.saml.saml2.core.Subject subject = assertion.getSubject();
                     if (validateAuthenticationSubject(subject, assertion.getID(), postBinding)) {
                         validateAudienceRestrictionCondition(assertion.getConditions());
    -                    foundValidSubject = true;
    +                    validAssertion = assertion;
                         // Store Session NotOnOrAfter
                         for (AuthnStatement authnStatment : assertion.getAuthnStatements()) {
                             if (authnStatment.getSessionNotOnOrAfter() != null) {
    @@ -123,10 +123,9 @@ public SSOValidatorResponse validateSamlResponse(
                         }
                     }
                 }
    -            
             }
             
    -        if (!foundValidSubject) {
    +        if (validAssertion == null) {
                 LOG.fine("The Response did not contain any Authentication Statement that matched "
                          + "the Subject Confirmation criteria");
                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
    @@ -140,7 +139,7 @@ public SSOValidatorResponse validateSamlResponse(
             }
             
             // the assumption for now is that SAMLResponse will contain only a single assertion
    -        Element assertionElement = samlResponse.getAssertions().get(0).getDOM();
    +        Element assertionElement = validAssertion.getDOM();
             Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
             validatorResponse.setAssertionElement(clonedAssertionElement);
             validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java+4 0 modified
    @@ -130,6 +130,10 @@ public void setSubjectLocality(String ipAddress, String dnsAddress) {
             this.subjectLocalityDnsAddress = dnsAddress;
         }
         
    +    public void setSubjectName(String subjectName) {
    +        this.subjectName = subjectName;
    +    }
    +    
         public void setResource(String resource) {
             this.resource = resource;
         }
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java+218 0 added
    @@ -0,0 +1,218 @@
    +/**
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements. See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership. The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License. You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied. See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +package org.apache.cxf.rs.security.saml.sso;
    +
    +import java.io.InputStream;
    +import java.security.KeyStore;
    +import java.util.Collections;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +
    +import org.apache.wss4j.common.crypto.Crypto;
    +import org.apache.wss4j.common.crypto.Merlin;
    +import org.apache.wss4j.common.saml.OpenSAMLUtil;
    +import org.apache.wss4j.common.saml.SAMLCallback;
    +import org.apache.wss4j.common.saml.SAMLUtil;
    +import org.apache.wss4j.common.saml.SamlAssertionWrapper;
    +import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
    +import org.apache.wss4j.common.saml.bean.ConditionsBean;
    +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
    +import org.apache.wss4j.common.saml.builder.SAML2Constants;
    +import org.apache.wss4j.common.util.Loader;
    +import org.apache.wss4j.dom.WSConstants;
    +import org.apache.wss4j.dom.WSSConfig;
    +import org.joda.time.DateTime;
    +import org.opensaml.saml.common.xml.SAMLConstants;
    +import org.opensaml.saml.saml2.core.Response;
    +import org.opensaml.saml.saml2.core.Status;
    +
    +/**
    + * Some unit tests for the SAMLProtocolResponseValidator and the SAMLSSOResponseValidator
    + */
    +public class CombinedValidatorTest extends org.junit.Assert {
    +    
    +    static {
    +        WSSConfig.init();
    +        OpenSAMLUtil.initSamlEngine();
    +    }
    +
    +    @org.junit.Test
    +    public void testSuccessfulValidation() throws Exception {
    +        
    +        Element responseElement = createResponse();
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    @org.junit.Test
    +    public void testWrappingAttack3() throws Exception {
    +        Element responseElement = createResponse();
    +        
    +        // Get Assertion Element
    +        Element assertionElement = 
    +            (Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
    +        assertNotNull(assertionElement);
    +        
    +        // Clone it, strip the Signature, modify the Subject, change Subj Conf
    +        Element clonedAssertion = (Element)assertionElement.cloneNode(true);
    +        clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
    +        Element sigElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
    +        clonedAssertion.removeChild(sigElement);
    +        
    +        Element subjElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
    +        Element subjNameIdElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
    +        subjNameIdElement.setTextContent("bob");
    +        
    +        Element subjConfElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
    +        subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
    +        
    +        // Now insert the modified cloned Assertion into the Response before actual assertion
    +        responseElement.insertBefore(clonedAssertion, assertionElement);
    +        
    +        // System.out.println(DOM2Writer.nodeToString(responseElement));
    +        
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    private Element createResponse() throws Exception {
    +        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    +        docBuilderFactory.setNamespaceAware(true);
    +        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    +        Document doc = docBuilder.newDocument();
    +        
    +        Status status = 
    +            SAML2PResponseComponentBuilder.createStatus(
    +                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
    +            );
    +        Response response = 
    +            SAML2PResponseComponentBuilder.createSAMLResponse(
    +                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
    +            );
    +        
    +        // Create an AuthenticationAssertion
    +        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
    +        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
    +        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
    +        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
    +        callbackHandler.setSubjectName("alice");
    +        
    +        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
    +        subjectConfirmationData.setAddress("http://apache.org");
    +        subjectConfirmationData.setInResponseTo("12345");
    +        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
    +        subjectConfirmationData.setRecipient("http://recipient.apache.org");
    +        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
    +        
    +        ConditionsBean conditions = new ConditionsBean();
    +        conditions.setNotBefore(new DateTime());
    +        conditions.setNotAfter(new DateTime().plusMinutes(5));
    +        
    +        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
    +        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
    +        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
    +        callbackHandler.setConditions(conditions);
    +        
    +        SAMLCallback samlCallback = new SAMLCallback();
    +        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
    +        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        assertion.signAssertion("alice", "password", issuerCrypto, false);
    +        
    +        response.getAssertions().add(assertion.getSaml2());
    +        
    +        Element policyElement = OpenSAMLUtil.toDom(response, doc);
    +        doc.appendChild(policyElement);
    +        assertNotNull(policyElement);
    +        
    +        return policyElement;
    +    }
    +}
    
1c2a53080004

Adding SAML SSO tests.

https://github.com/apache/cxfColm O hEigeartaighJul 30, 2015via ghsa
3 files changed · +226 5
  • rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java+4 5 modified
    @@ -92,7 +92,7 @@ public SSOValidatorResponse validateSamlResponse(
             }
             
             // Validate Assertions
    -        boolean foundValidSubject = false;
    +        org.opensaml.saml.saml2.core.Assertion validAssertion = null;
             Date sessionNotOnOrAfter = null;
             for (org.opensaml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
                 // Check the Issuer
    @@ -114,7 +114,7 @@ public SSOValidatorResponse validateSamlResponse(
                     org.opensaml.saml2.core.Subject subject = assertion.getSubject();
                     if (validateAuthenticationSubject(subject, assertion.getID(), postBinding)) {
                         validateAudienceRestrictionCondition(assertion.getConditions());
    -                    foundValidSubject = true;
    +                    validAssertion = assertion;
                         // Store Session NotOnOrAfter
                         for (AuthnStatement authnStatment : assertion.getAuthnStatements()) {
                             if (authnStatment.getSessionNotOnOrAfter() != null) {
    @@ -123,10 +123,9 @@ public SSOValidatorResponse validateSamlResponse(
                         }
                     }
                 }
    -            
             }
             
    -        if (!foundValidSubject) {
    +        if (validAssertion == null) {
                 LOG.fine("The Response did not contain any Authentication Statement that matched "
                          + "the Subject Confirmation criteria");
                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
    @@ -140,7 +139,7 @@ public SSOValidatorResponse validateSamlResponse(
             }
             
             // the assumption for now is that SAMLResponse will contain only a single assertion
    -        Element assertionElement = samlResponse.getAssertions().get(0).getDOM();
    +        Element assertionElement = validAssertion.getDOM();
             Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
             validatorResponse.setAssertionElement(clonedAssertionElement);
             validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java+4 0 modified
    @@ -130,6 +130,10 @@ public void setSubjectLocality(String ipAddress, String dnsAddress) {
             this.subjectLocalityDnsAddress = dnsAddress;
         }
         
    +    public void setSubjectName(String subjectName) {
    +        this.subjectName = subjectName;
    +    }
    +    
         public void setResource(String resource) {
             this.resource = resource;
         }
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java+218 0 added
    @@ -0,0 +1,218 @@
    +/**
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements. See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership. The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License. You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied. See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +package org.apache.cxf.rs.security.saml.sso;
    +
    +import java.io.InputStream;
    +import java.security.KeyStore;
    +import java.util.Collections;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +
    +import org.apache.wss4j.common.crypto.Crypto;
    +import org.apache.wss4j.common.crypto.Merlin;
    +import org.apache.wss4j.common.saml.OpenSAMLUtil;
    +import org.apache.wss4j.common.saml.SAMLCallback;
    +import org.apache.wss4j.common.saml.SAMLUtil;
    +import org.apache.wss4j.common.saml.SamlAssertionWrapper;
    +import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
    +import org.apache.wss4j.common.saml.bean.ConditionsBean;
    +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
    +import org.apache.wss4j.common.saml.builder.SAML2Constants;
    +import org.apache.wss4j.common.util.Loader;
    +import org.apache.wss4j.dom.WSConstants;
    +import org.apache.wss4j.dom.WSSConfig;
    +import org.joda.time.DateTime;
    +import org.opensaml.saml.common.xml.SAMLConstants;
    +import org.opensaml.saml.saml2.core.Response;
    +import org.opensaml.saml.saml2.core.Status;
    +
    +/**
    + * Some unit tests for the SAMLProtocolResponseValidator and the SAMLSSOResponseValidator
    + */
    +public class CombinedValidatorTest extends org.junit.Assert {
    +    
    +    static {
    +        WSSConfig.init();
    +        OpenSAMLUtil.initSamlEngine();
    +    }
    +
    +    @org.junit.Test
    +    public void testSuccessfulValidation() throws Exception {
    +        
    +        Element responseElement = createResponse();
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    @org.junit.Test
    +    public void testWrappingAttack3() throws Exception {
    +        Element responseElement = createResponse();
    +        
    +        // Get Assertion Element
    +        Element assertionElement = 
    +            (Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
    +        assertNotNull(assertionElement);
    +        
    +        // Clone it, strip the Signature, modify the Subject, change Subj Conf
    +        Element clonedAssertion = (Element)assertionElement.cloneNode(true);
    +        clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
    +        Element sigElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
    +        clonedAssertion.removeChild(sigElement);
    +        
    +        Element subjElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
    +        Element subjNameIdElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
    +        subjNameIdElement.setTextContent("bob");
    +        
    +        Element subjConfElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
    +        subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
    +        
    +        // Now insert the modified cloned Assertion into the Response before actual assertion
    +        responseElement.insertBefore(clonedAssertion, assertionElement);
    +        
    +        // System.out.println(DOM2Writer.nodeToString(responseElement));
    +        
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    private Element createResponse() throws Exception {
    +        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    +        docBuilderFactory.setNamespaceAware(true);
    +        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    +        Document doc = docBuilder.newDocument();
    +        
    +        Status status = 
    +            SAML2PResponseComponentBuilder.createStatus(
    +                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
    +            );
    +        Response response = 
    +            SAML2PResponseComponentBuilder.createSAMLResponse(
    +                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
    +            );
    +        
    +        // Create an AuthenticationAssertion
    +        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
    +        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
    +        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
    +        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
    +        callbackHandler.setSubjectName("alice");
    +        
    +        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
    +        subjectConfirmationData.setAddress("http://apache.org");
    +        subjectConfirmationData.setInResponseTo("12345");
    +        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
    +        subjectConfirmationData.setRecipient("http://recipient.apache.org");
    +        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
    +        
    +        ConditionsBean conditions = new ConditionsBean();
    +        conditions.setNotBefore(new DateTime());
    +        conditions.setNotAfter(new DateTime().plusMinutes(5));
    +        
    +        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
    +        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
    +        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
    +        callbackHandler.setConditions(conditions);
    +        
    +        SAMLCallback samlCallback = new SAMLCallback();
    +        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
    +        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        assertion.signAssertion("alice", "password", issuerCrypto, false);
    +        
    +        response.getAssertions().add(assertion.getSaml2());
    +        
    +        Element policyElement = OpenSAMLUtil.toDom(response, doc);
    +        doc.appendChild(policyElement);
    +        assertNotNull(policyElement);
    +        
    +        return policyElement;
    +    }
    +}
    
02245c656941

Adding SAML SSO tests.

https://github.com/apache/cxfColm O hEigeartaighJul 30, 2015via ghsa
3 files changed · +233 4
  • rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/SAMLSSOResponseValidator.java+11 4 modified
    @@ -92,7 +92,7 @@ public SSOValidatorResponse validateSamlResponse(
             }
             
             // Validate Assertions
    -        boolean foundValidSubject = false;
    +        org.opensaml.saml.saml2.core.Assertion validAssertion = null;
             Date sessionNotOnOrAfter = null;
             for (org.opensaml.saml2.core.Assertion assertion : samlResponse.getAssertions()) {
                 // Check the Issuer
    @@ -114,7 +114,7 @@ public SSOValidatorResponse validateSamlResponse(
                     org.opensaml.saml2.core.Subject subject = assertion.getSubject();
                     if (validateAuthenticationSubject(subject, assertion.getID(), postBinding)) {
                         validateAudienceRestrictionCondition(assertion.getConditions());
    -                    foundValidSubject = true;
    +                    validAssertion = assertion;
                         // Store Session NotOnOrAfter
                         for (AuthnStatement authnStatment : assertion.getAuthnStatements()) {
                             if (authnStatment.getSessionNotOnOrAfter() != null) {
    @@ -123,10 +123,9 @@ public SSOValidatorResponse validateSamlResponse(
                         }
                     }
                 }
    -            
             }
             
    -        if (!foundValidSubject) {
    +        if (validAssertion == null) {
                 LOG.fine("The Response did not contain any Authentication Statement that matched "
                          + "the Subject Confirmation criteria");
                 throw new WSSecurityException(WSSecurityException.FAILURE, "invalidSAMLsecurity");
    @@ -136,8 +135,16 @@ public SSOValidatorResponse validateSamlResponse(
             validatorResponse.setResponseId(samlResponse.getID());
             validatorResponse.setSessionNotOnOrAfter(sessionNotOnOrAfter);
             // the assumption for now is that SAMLResponse will contain only a single assertion
    +<<<<<<< HEAD
             Element assertionElement = samlResponse.getAssertions().get(0).getDOM();
             validatorResponse.setAssertion(DOM2Writer.nodeToString(assertionElement.cloneNode(true)));
    +=======
    +        Element assertionElement = validAssertion.getDOM();
    +        Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
    +        validatorResponse.setAssertionElement(clonedAssertionElement);
    +        validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));
    +        
    +>>>>>>> 1c2a530... Adding SAML SSO tests.
             return validatorResponse;
         }
         
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/AbstractSAMLCallbackHandler.java+4 0 modified
    @@ -132,6 +132,10 @@ public void setSubjectLocality(String ipAddress, String dnsAddress) {
             this.subjectLocalityDnsAddress = dnsAddress;
         }
         
    +    public void setSubjectName(String subjectName) {
    +        this.subjectName = subjectName;
    +    }
    +    
         public void setResource(String resource) {
             this.resource = resource;
         }
    
  • rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/CombinedValidatorTest.java+218 0 added
    @@ -0,0 +1,218 @@
    +/**
    + * Licensed to the Apache Software Foundation (ASF) under one
    + * or more contributor license agreements. See the NOTICE file
    + * distributed with this work for additional information
    + * regarding copyright ownership. The ASF licenses this file
    + * to you under the Apache License, Version 2.0 (the
    + * "License"); you may not use this file except in compliance
    + * with the License. You may obtain a copy of the License at
    + *
    + * http://www.apache.org/licenses/LICENSE-2.0
    + *
    + * Unless required by applicable law or agreed to in writing,
    + * software distributed under the License is distributed on an
    + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    + * KIND, either express or implied. See the License for the
    + * specific language governing permissions and limitations
    + * under the License.
    + */
    +
    +package org.apache.cxf.rs.security.saml.sso;
    +
    +import java.io.InputStream;
    +import java.security.KeyStore;
    +import java.util.Collections;
    +
    +import javax.xml.parsers.DocumentBuilder;
    +import javax.xml.parsers.DocumentBuilderFactory;
    +
    +import org.w3c.dom.Document;
    +import org.w3c.dom.Element;
    +
    +import org.apache.wss4j.common.crypto.Crypto;
    +import org.apache.wss4j.common.crypto.Merlin;
    +import org.apache.wss4j.common.saml.OpenSAMLUtil;
    +import org.apache.wss4j.common.saml.SAMLCallback;
    +import org.apache.wss4j.common.saml.SAMLUtil;
    +import org.apache.wss4j.common.saml.SamlAssertionWrapper;
    +import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
    +import org.apache.wss4j.common.saml.bean.ConditionsBean;
    +import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
    +import org.apache.wss4j.common.saml.builder.SAML2Constants;
    +import org.apache.wss4j.common.util.Loader;
    +import org.apache.wss4j.dom.WSConstants;
    +import org.apache.wss4j.dom.WSSConfig;
    +import org.joda.time.DateTime;
    +import org.opensaml.saml.common.xml.SAMLConstants;
    +import org.opensaml.saml.saml2.core.Response;
    +import org.opensaml.saml.saml2.core.Status;
    +
    +/**
    + * Some unit tests for the SAMLProtocolResponseValidator and the SAMLSSOResponseValidator
    + */
    +public class CombinedValidatorTest extends org.junit.Assert {
    +    
    +    static {
    +        WSSConfig.init();
    +        OpenSAMLUtil.initSamlEngine();
    +    }
    +
    +    @org.junit.Test
    +    public void testSuccessfulValidation() throws Exception {
    +        
    +        Element responseElement = createResponse();
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    @org.junit.Test
    +    public void testWrappingAttack3() throws Exception {
    +        Element responseElement = createResponse();
    +        
    +        // Get Assertion Element
    +        Element assertionElement = 
    +            (Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
    +        assertNotNull(assertionElement);
    +        
    +        // Clone it, strip the Signature, modify the Subject, change Subj Conf
    +        Element clonedAssertion = (Element)assertionElement.cloneNode(true);
    +        clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
    +        Element sigElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
    +        clonedAssertion.removeChild(sigElement);
    +        
    +        Element subjElement = 
    +            (Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
    +        Element subjNameIdElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
    +        subjNameIdElement.setTextContent("bob");
    +        
    +        Element subjConfElement = 
    +            (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
    +        subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
    +        
    +        // Now insert the modified cloned Assertion into the Response before actual assertion
    +        responseElement.insertBefore(clonedAssertion, assertionElement);
    +        
    +        // System.out.println(DOM2Writer.nodeToString(responseElement));
    +        
    +        Response marshalledResponse = (Response)OpenSAMLUtil.fromDom(responseElement);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        // Validate the Response
    +        SAMLProtocolResponseValidator validator = new SAMLProtocolResponseValidator();
    +        validator.validateSamlResponse(
    +            marshalledResponse, issuerCrypto, new KeystorePasswordCallback()
    +        );
    +        
    +        // Test SSO validation
    +        SAMLSSOResponseValidator ssoValidator = new SAMLSSOResponseValidator();
    +        ssoValidator.setIssuerIDP("http://cxf.apache.org/issuer");
    +        ssoValidator.setAssertionConsumerURL("http://recipient.apache.org");
    +        ssoValidator.setClientAddress("http://apache.org");
    +        ssoValidator.setRequestId("12345");
    +        ssoValidator.setSpIdentifier("http://service.apache.org");
    +        
    +        // Parse the response
    +        SSOValidatorResponse ssoResponse = 
    +            ssoValidator.validateSamlResponse(marshalledResponse, false);
    +        SamlAssertionWrapper parsedAssertion = 
    +            new SamlAssertionWrapper(ssoResponse.getAssertionElement());
    +        
    +        assertEquals("alice", parsedAssertion.getSubjectName());
    +    }
    +    
    +    private Element createResponse() throws Exception {
    +        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    +        docBuilderFactory.setNamespaceAware(true);
    +        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
    +        Document doc = docBuilder.newDocument();
    +        
    +        Status status = 
    +            SAML2PResponseComponentBuilder.createStatus(
    +                SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
    +            );
    +        Response response = 
    +            SAML2PResponseComponentBuilder.createSAMLResponse(
    +                "http://cxf.apache.org/saml", "http://cxf.apache.org/issuer", status
    +            );
    +        
    +        // Create an AuthenticationAssertion
    +        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
    +        callbackHandler.setStatement(SAML2CallbackHandler.Statement.AUTHN);
    +        callbackHandler.setIssuer("http://cxf.apache.org/issuer");
    +        callbackHandler.setConfirmationMethod(SAML2Constants.CONF_BEARER);
    +        callbackHandler.setSubjectName("alice");
    +        
    +        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
    +        subjectConfirmationData.setAddress("http://apache.org");
    +        subjectConfirmationData.setInResponseTo("12345");
    +        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
    +        subjectConfirmationData.setRecipient("http://recipient.apache.org");
    +        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
    +        
    +        ConditionsBean conditions = new ConditionsBean();
    +        conditions.setNotBefore(new DateTime());
    +        conditions.setNotAfter(new DateTime().plusMinutes(5));
    +        
    +        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
    +        audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
    +        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
    +        callbackHandler.setConditions(conditions);
    +        
    +        SAMLCallback samlCallback = new SAMLCallback();
    +        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
    +        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
    +        
    +        Crypto issuerCrypto = new Merlin();
    +        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    +        ClassLoader loader = Loader.getClassLoader(CombinedValidatorTest.class);
    +        InputStream input = Merlin.loadInputStream(loader, "alice.jks");
    +        keyStore.load(input, "password".toCharArray());
    +        ((Merlin)issuerCrypto).setKeyStore(keyStore);
    +        
    +        assertion.signAssertion("alice", "password", issuerCrypto, false);
    +        
    +        response.getAssertions().add(assertion.getSaml2());
    +        
    +        Element policyElement = OpenSAMLUtil.toDom(response, doc);
    +        doc.appendChild(policyElement);
    +        assertNotNull(policyElement);
    +        
    +        return policyElement;
    +    }
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

23

News mentions

0

No linked articles in our index yet.