Skip to content

Regression: SAML Authentication fails when EncryptedAssertion is signed but response is not signed #10162

Description

@sarod

Describe the bug

With spring-security 5.5.0+ authenticating with SAML fails with Saml2AuthenticationException{error=[malformed_response_data] No assertions found in response.} when EncryptedAssertion is signed but response is not signed.

Similar response authenticates properly with spring-security 5.4.6.

To Reproduce
Configure SAML response to Encrypt & Sign Assertion but keep the message/response unsigned.
Try to authenticate with OpenSamlAuthenticationProvider or OpenSaml4AuthenticationProvider.

Alternatively the following test method can be added to OpenSamlAuthenticationProviderTests to reproduce.

	@Test
	public void authenticateWhenEncryptedAssertionWithSignatureAndNoResponseSignatureThenItSucceeds() {
		Response response = response();
		Assertion assertion = TestOpenSamlObjects.signed(assertion(),
				TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID);
		EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion,
				TestSaml2X509Credentials.assertingPartyEncryptingCredential());
		response.getEncryptedAssertions().add(encryptedAssertion);
		Saml2AuthenticationToken token = token(response, decrypting(verifying(registration())));
		this.provider.authenticate(token);
	}

NOTE: with current main branch it seems that gradle tasks opensaml3Test and opensaml4Test don't seem to run any tests. This may be user error because I'm not a gradle expert.
In any case I add to move all the code & tests back to the main and test sourcesets to be able to run the tests.

Expected behavior
Authentication should proceed without errors.

Analysis

In 5.4.6
The method OpenSamlAuthenticationProvider.decryptAssertions is called even if Response is not signed.
This mutates the Response.getAssertions() list by adding the decrypted assertions.
Then OpenSamlAuthenticationProvider#validateAssertions checks that some assertion exist with

List<Assertion> assertions = response.getAssertions();
if (assertions.isEmpty()) {
    throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA,
            "No assertions found in response.", null);
}

In 5.5
In 5.5 the decryption logic has been moved to responseElementsDecrypter but the decryption is only called if response is signed.

boolean responseSigned = response.isSigned();
if (responseSigned) {
    this.responseElementsDecrypter.accept(responseToken);
}

So at this stage the Response.getAssertions() contains the decrypted assertions only if the response is signed.

Then the responseSignatureValidator is called which does

if (response.getAssertions().isEmpty()) {
    throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA,
            "No assertions found in response.", null);
}

And this throws the exception.

Modifying code to always decrypting seems to fix the issue.

Metadata

Metadata

Assignees

Labels

in: saml2An issue in SAML2 modulesstatus: declinedA suggestion or change that we don't feel we should currently applytype: bugA general bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions