-
Notifications
You must be signed in to change notification settings - Fork 25.2k
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
Add Issuer to failed SAML Signature validation logs when available #126310
Conversation
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); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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())) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
...curity/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java
Outdated
Show resolved
Hide resolved
Hi @richard-dennehy, I've created a changelog YAML for you. |
There was a problem hiding this 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 -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the warning in this method also include the issuer
? https://github.com/elastic/elasticsearch/pull/126310/files#diff-19351c3f19f00d928645d11a61ef3c3dea10982b13f8f7ed47773c18d034b33dR203
And maybe here too: https://github.com/elastic/elasticsearch/pull/126310/files#diff-19351c3f19f00d928645d11a61ef3c3dea10982b13f8f7ed47773c18d034b33dL234
...curity/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java
Outdated
Show resolved
Hide resolved
...curity/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java
Outdated
Show resolved
Hide resolved
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())) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 {}", |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Nit thing about the label. Since this is a Realm change, this could be labeled as |
Pinging @elastic/es-security (Team:Security) |
There was a problem hiding this 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.
...in/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlObjectHandler.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
private String formatIssuer(Issuer issuer) { | ||
return issuer != null ? String.format(" [%s]", issuer.getValue()) : ""; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
++
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
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
There was a problem hiding this 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)); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
…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)
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation |
…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)
…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)
…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)
…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)
…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)
…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)
…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)
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.