Skip to content

Add Issuer to failed SAML Signature validation logs when available #126310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

richard-dennehy
Copy link
Contributor

Resolves #111022

Includes Issuer in warning logs when SAML Signature validation fails (when available) to make it clearer that these logs are false positives and can be ignored when multiple realms are configured.

final String msg = "SAML Signature [{}] could not be validated against [{}]";
return samlException(msg, signature, describeCredentials(credentials));
private ElasticsearchSecurityException samlSignatureException(Issuer issuer, List<Credential> credentials, String signature) {
return samlSignatureException(issuer, credentials, signature, null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove duplication (since I need to edit the exact same log message twice)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@@ -423,7 +429,7 @@ private void validateSignature(String inputString, String signatureAlgorithm, St
);
return false;
}
}, signatureText);
}, signatureText, null);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the context of this is SAML Logout with a query string; the message gets parsed into a SAML document after the signature has been verified, so I'm not sure it's possible to pass an Issuer here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. We could change the order of things here so the issuer is parsed from the message before throwing the error, but if that parsing fails, it ends up causing more confusion than help so I think it makes sense to leave it as it.

assertThat(exception.getMessage(), containsString("could not be validated"));
assertThat(exception.getCause(), nullValue());
assertThat(SamlUtils.isSamlException(exception), is(true));
try (var mockLog = MockLog.capture(authenticator.getClass())) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update existing tests to check for log message

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@richard-dennehy richard-dennehy added >enhancement :Security/Security Security issues without another label Team:Security Meta label for security team labels Apr 4, 2025
@elasticsearchmachine
Copy link
Collaborator

Hi @richard-dennehy, I've created a changelog YAML for you.

Copy link
Contributor

@jfreden jfreden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job on this PR! I've left a couple of small comments. I think the CI failure happens because the branch is outdated.

Can you also create a PR against the docs-content repo as suggested here: #111022 (comment)

I think this PR can be moved to "Ready for Review".

final String signatureText = text(signature, 32);
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
try {
profileValidator.validate(signature);
} catch (SignatureException e) {
throw samlSignatureException(idp.getSigningCredentials(), signatureText, e);
throw samlSignatureException(issuer, idp.getSigningCredentials(), signatureText, e);
}

checkIdpSignature(credential -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertThat(exception.getMessage(), containsString("could not be validated"));
assertThat(exception.getCause(), nullValue());
assertThat(SamlUtils.isSamlException(exception), is(true));
try (var mockLog = MockLog.capture(authenticator.getClass())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@@ -423,7 +429,7 @@ private void validateSignature(String inputString, String signatureAlgorithm, St
);
return false;
}
}, signatureText);
}, signatureText, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. We could change the order of things here so the issuer is parsed from the message before throwing the error, but if that parsing fails, it ends up causing more confusion than help so I think it makes sense to leave it as it.

"The XML Signature of this SAML message cannot be validated. Please verify that the saml realm uses the correct SAML"
+ "metadata file/URL for this Identity Provider"
"The XML Signature of this SAML message cannot be validated. Please verify that the saml realm uses the correct SAML "
+ "metadata file/URL for this Identity Provider {}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A convention for logging is:

If the log message includes values from your code then you must use placeholders rather than constructing the string yourself using simple concatenation. Consider wrapping the values in [...] to help distinguish them from the static part of the message.

Can you add that around issuerValue (maybe at the null check?)?

final String msg = "SAML Signature [{}] could not be validated against [{}]";
return samlException(msg, signature, describeCredentials(credentials));
private ElasticsearchSecurityException samlSignatureException(Issuer issuer, List<Credential> credentials, String signature) {
return samlSignatureException(issuer, credentials, signature, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@jfreden
Copy link
Contributor

jfreden commented Apr 7, 2025

Nit thing about the label. Since this is a Realm change, this could be labeled as :Security/Authentication

@richard-dennehy richard-dennehy added the :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) label Apr 7, 2025
@richard-dennehy richard-dennehy marked this pull request as ready for review April 7, 2025 09:21
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

Copy link
Contributor

@n1v0lg n1v0lg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! I'll leave the final review to Johannes -- just adding some additional thoughts/suggestions.

}

private String formatIssuer(Issuer issuer) {
return issuer != null ? String.format(" [%s]", issuer.getValue()) : "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make it clear that the issuer value is a value we parsed from the request -- esp. since we're logging it in the context where signature verification failed it can potentially be an arbitrary, untrusted value so we should identify that in the log message -- something like "The issuer included in the SAML message was [{}]."

Also, instead of "" I'd go for an explicit value like <missing-issuer> or <null>.

We could call this method describeIssuer to match describeCredentials

Copy link
Contributor

@n1v0lg n1v0lg Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thought here:

@jfreden @richard-dennehy: WDYT about limiting the length here with a call to cleanTruncate (with a ... if we actually truncated) to avoid excessively long log lines/exception messages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it be more confusing to explicitly describe the issuer as null? In the cases where issuer is null, all it means is we haven't parsed it from the request yet

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, instead of "" I'd go for an explicit value like or .

For the logout flow, the issuer is probably included (so not missing, we just haven't parsed it yet) and if it's not, validation will fail later (if the signature is correct). Because of that, I think "" is acceptable in this case. My thinking is that null as issuer probably won't happen or at least be extremely rare in production and if it happens, validation will fail because it's a required field.

@jfreden @richard-dennehy: WDYT about limiting the length here with a call to cleanTruncate (with a ... if we actually truncated) to avoid excessively long log lines/exception messages?

Is that necessary for the issuer? Are you worried the issuer is a very long url?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, good point -- for logout requests we always pass null. Okay, in that case let's go with your approach of not including information about the issuer if the value is null (but the more informative message if it available). We could come up with something more nuanced where we call different methods depending on whether it's a null by definition vs. a null by parsing but no need to over-engineer this thing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could call this method describeIssuer to match describeCredentials

++

Copy link
Contributor

@n1v0lg n1v0lg Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that necessary for the issuer? Are you worried the issuer is a very long url?

Worried primarily about processing a value that's arbitrarily malformed since it's not from a trusted source (we log in cases where the signature verification failed). Limiting it to some high-ish length value would not impede UX but prevent unforeseen issues around large values. It's a smaller concern since for the cases where the value is available, we've already parsed the entire SAML message and thus done much of the heavy lifting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying! Looks like neither opensaml or the saml spec limits the size of the issuer field, so I think adding a cleanTruncate call makes sense.

}
}

public void testDescribeNullIssuer() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some tests for describeIssuer since it's getting a little complicated

return "";
}
final String msg = " The issuer included in the SAML message was [%s]";
if (issuer.getValue().length() > 64) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issuer.getValue() can return null, as I discovered writing the tests - how realistic do we think this is? Should I guard against it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, lets guard against null here 👍

Copy link
Contributor

@n1v0lg n1v0lg Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd move the 64 into a constant and make the threshold higher -- 512 is still negligible in terms of a logging payload but less likely to truncate useful info

Copy link
Contributor

@jfreden jfreden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM pending CI! Just a couple of nits. Good job!


public void testDescribeVeryLongIssuer() {
final Issuer issuer = new IssuerBuilder().buildObject();
issuer.setValue("https://idp.saml.elastic.test/" + "a".repeat(128));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: there is a utility function called randomAlphaOfLength(128) that could be used here.

}

public void testDescribeNullIssuerValue() {
final Issuer issuer = new IssuerBuilder().buildObject();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: testDescribeNullIssuer and this one can be combined using randomFrom(null, new IssuerBuilder().buildObject())

}

// package private for testing
String describeIssuer(@Nullable Issuer issuer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this could be static.

@jfreden jfreden removed the :Security/Security Security issues without another label label Apr 7, 2025
@richard-dennehy richard-dennehy merged commit ceaa01a into elastic:main Apr 8, 2025
22 checks passed
@richard-dennehy richard-dennehy deleted the saml-issuer-in-warn-logs branch April 8, 2025 09:50
richard-dennehy added a commit to richard-dennehy/elasticsearch that referenced this pull request Apr 23, 2025
…lastic#126310)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
@richard-dennehy
Copy link
Contributor Author

💚 All backports created successfully

Status Branch Result
8.x
9.0
8.18
8.17

Questions ?

Please refer to the Backport tool documentation

richard-dennehy added a commit to richard-dennehy/elasticsearch that referenced this pull request Apr 23, 2025
…lastic#126310)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
richard-dennehy added a commit to richard-dennehy/elasticsearch that referenced this pull request Apr 23, 2025
…lastic#126310)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
richard-dennehy added a commit to richard-dennehy/elasticsearch that referenced this pull request Apr 23, 2025
…lastic#126310)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
elasticsearchmachine added a commit that referenced this pull request Apr 23, 2025
…126310) (#127218)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
elasticsearchmachine added a commit that referenced this pull request Apr 23, 2025
…126310) (#127220)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
elasticsearchmachine added a commit that referenced this pull request Apr 23, 2025
…126310) (#127221)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
elasticsearchmachine added a commit that referenced this pull request Apr 23, 2025
…126310) (#127219)

* Add Issuer to failed SAML Signature validation logs when available

* [CI] Auto commit changes from spotless

* Fix tests

* Update docs/changelog/126310.yaml

* address review comments

* replace String.format call

* update formatIssuer to describeIssuer

* [CI] Auto commit changes from spotless

* truncate long issuers in log messages

* [CI] Auto commit changes from spotless

* handle null issuer value

* address review comments

---------

Co-authored-by: elasticsearchmachine <[email protected]>
(cherry picked from commit ceaa01a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>enhancement :Security/Authentication Logging in, Usernames/passwords, Realms (Native/LDAP/AD/SAML/PKI/etc) Team:Security Meta label for security team v8.17.6 v8.18.1 v8.19.0 v9.0.1 v9.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Issuer to SAML WARN log events
4 participants