Handling org.jasig.cas.client.validation.TicketValidationException - spring-security

Our spring boot web application uses CAS authentication. We are undergoing a security scan and one of the test cases issued a log in with an invalid CAS ticket. The test case was: "/Logon?blockbanner=true&ticket=ST-3582-f3sADdmKlq"
Since this ticket is not valid, CAS produced a warning (see below) and the result is Http 500 which the security team failed us on.
I attempted to capture this string in our spring security configuration:
.regexMatchers(".+blockbanner=true.+").denyAll()
However, if we log in normally and a valid ticket is returned, we cannot get inside the application!!!
Is there a way to capture and handle the warning from CAS so that we can produce a better Http error such as Http 401, etc.? Thank you!
CAS warning
020-04-17 11:22:26,913 WARN o.j.c.c.v.Saml11TicketValidationFilter[AbstractTicketValidationFilter.java:189] doFilter: org.jasig.cas.client.validation.TicketValidationException: org.opensaml.SAMLException: ticket 'ST-155-g1lYXqTfB0xAOAp2RrU7-tecs_cas_sat' not recognized
org.jasig.cas.client.validation.TicketValidationException: org.opensaml.SAMLException: ticket 'ST-155-g1lYXqTfB0xAOAp2RrU7-tecs_cas_sat' not recognized
at org.jasig.cas.client.validation.Saml11TicketValidator.parseResponseFromServer(Saml11TicketValidator.java:115)
at org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:217)
at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:169)

So, I found the solution to this. I added the line:
filter.setExceptionOnValidationFailure(false);
so that CAS does not generate warning when it gets an invalid ticket.
So, instead of Http 500, it returned Http 403 which passed the security scan test.
Saml11TicketValidationFilter filter = new Saml11TicketValidationFilter();
beanFactory.autowireBean(filter);
filter.setIgnoreInitConfiguration(true);
filter.setRedirectAfterValidation(true);
filter.setArtifactParameterName("ticket");
filter.setServerName(serverName);
filter.setExceptionOnValidationFailure(false);

Related

Spring Saml2 and Spring Session - SavedRequest not retrieved (cannot redirect to requested page after authentication / InResponseTo exception)

I am trying to use Spring Boot SAML2 + Spring Session to secure my web application (to be deployed on K8S). Everything is fine without spring-session-data-rest or spring-session-hazelcast. It can authenticate with Okta and redirect back to the requested page after authentication. Also, I can use either opensaml3 or opensaml4.
However, when I tried to use either spring-session-data-rest or spring-session-hazelcast (just 1 instance, no cluster yet), it would not redirect back to the requested page. Also, it'd fail with opensaml4 with exception: "The response contained an InResponseTo attribute [] but no saved authentication request was found". There's some mentioning about opensaml3 going EOL so I want to make it work with opensaml4.
Here's a sample application to demonstrate my case https://github.com/simonckw/redis-saml2/tree/redis. Have I missed anything? Have anyone got a working sample with this setup? Help is much appreciated.
p.s. I've traced into HttpSessionRequestCache.java, invoked from SavedRequestAwareWarpper.java. Without spring-session-data-rest or spring-session-hazelcast, the saved request can be retrieved but not when either spring-session-data-rest or spring-session-hazelcast is enabled. It also seems to me that the InResponseTo exception could be related too. My Redis setup should be fine. Here's the session data written into Redis:
"spring:session:sessions:expires:7c1858d1-0ea7-4a7a-8523-2abf89137771"
"spring:session:expirations:1654439580000"
"spring:session:sessions:expires:58a584d3-625e-4e0a-bef5-3aaff485ad93"
"spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:abc#xyz.com"
"spring:session:sessions:7c1858d1-0ea7-4a7a-8523-2abf89137771"
"spring:session:sessions:58a584d3-625e-4e0a-bef5-3aaff485ad93"
127.0.0.1:6379> hkeys spring:session:sessions:7c1858d1-0ea7-4a7a-8523-2abf89137771
"sessionAttr:SPRING_SECURITY_SAVED_REQUEST"
"lastAccessedTime"
"maxInactiveInterval"
"creationTime"
"sessionAttr:org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository.SAML2_AUTHN_REQUEST"
127.0.0.1:6379> hkeys spring:session:sessions:58a584d3-625e-4e0a-bef5-3aaff485ad93
"maxInactiveInterval"
"creationTime"
"lastAccessedTime"
"sessionAttr:SPRING_SECURITY_CONTEXT"
"sessionAttr:SPRING_SECURITY_LAST_EXCEPTION"
The problem is that Spring Session is setting SameSite=Lax for its SESSION cookie. Your servlet container isn't setting that on JSESSIONID (when you're not using Spring Session).
Because the SAML response is being POSTed from Okta's page, the browser will not send the cookie, so Spring doesn't think it has a session in which to find the authentication request. It uses that saved request to reconcile the InResponseTo attribute.
You can work around this by removing SameSite from the cookie. Create a bean like this:
#Bean
public DefaultCookieSerializerCustomizer cookieSerializerCustomizer() {
return cookieSerializer -> {
cookieSerializer.setSameSite(null);
};
}
Alternatively, you could explicitly specify None, but then you would have to also set the Secure attribute.
Note: Chrome is supposed to default to Lax where SameSite isn't specified. In reality, it doesn't do that if HttpOnly is set. Safari and Firefox don't even seem to care about HttpOnly.
This problem is discussed here.

JMeter, bearer token and webdriver. "The audience is not valid"

I am working on setting up a load test suite for a site which has an OAuth2 login mechanism on another server (PKCE I believe it is), so when I click login-button I am tranfered to another site to perform the actual login, which then transfers med back to the main site.
This login site takes my credentials and in return gives the browser a code_challenge and some .js-files, from which I believe the browser later on create a Bearer token and place it in the browsers Session Storage.
This, as I understand it, cannot be replicated in JMeter alone, but it can be done if using Webdriver. Therefore I've set up a webdriver testcase in JMeter which performs the login and saves the used state, code_challenge and Bearer token in JMeter User Variables to be used later on in the load test (this all works fine).
The issue here is when I try to use the bearer token in a JMeter HTTP(S) Request I get the following error in response header:
WWW-Authenticate: Bearer error="invalid_token", error_description="The audience '<censored>' is invalid"
These are the headers for one of the GET requests with bearer token (which is previously recorded)
The bearer is collected with this piece of code
It doesn't matter if I'm using Firefox or Chrome webdrivers, the issue is the same.
So either I am trying to do something which cannot be done (re-use generated Bearer from Webdriver in JMeter), or I am missing something I do not understand. Help please?
I think you need to add Bearer to the value of the Authorization header like:
Also I don't think you need to kick off the real browser, well-behaved JMeter script is supposed to act like a real user using a real browser so my expectation is that you should be able to extract the token using HTTP Request samplers and suitable Post-Processors
For a single thread (functional test) the browser is okayish, but modern browsers are very resource intensive (one instances takes a CPU core and a couple of gigabytes of RAM) so when it comes to real load test execution you will need really powerful hardware in order to handle this authentication mechanism so it's better to stick to JMeter's HTTP Request samplers
I found the issue!
I had extracted the id_token and not access_token from the Session storage which of course was not valid as a Bearer token. They looked very much alike but was not the same, and I missed that.

Shibboleth SSO and Spring SP: Unable to login due to "InResponseToField" mismatch error

In my production setup with 2 Service Providers and 2 IdP instances behind a load balancer, I'm seeing the following error in one of my SP's logs and I'm not sure why:
InResponseToField of the Response doesn't correspond to sent message
I'm using Shibboleth 3, Service Provider of Spring security 3.1.2 RELEASE, Spring Security SAML 1.0.0.
I haven't been able to consistently reproduce this error on Production, since it can happen either when a user clicks a link and needs to be re-authenticated or when a user is on the log in screen.
The way I've been able to reproduce consistently so far is by removing the JSESSIONID of the Service Provider (by going to the Chrome console, for example, and deleting it from the cookie list) just before hitting submit on the login screen.
Below is a snippet of the logs for one of the SP's when this error occurs in Production - the service provider is creating a different JSessionID after authentication, similar to the setup I've described above:
2018-03-05 06:33:38 DEBUG HttpSessionStorage:93 - Storing message a7a636i3hee345244e5j390hia90fg to session BC1B9DEC1CF797AA99FF3F8B4431D301
2018-03-05 06:34:42 DEBUG HttpSessionStorage:117 - Message a7a636i3hee345244e5j390hia90fg not found in session FE9D2450284C49549E7AC212F1271045
org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message a7a636i3hee345244e5j390hia90fg
at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImpl.java:139)
Possible Solution with Security Issue
One possible solution is to disable certain request validation checks on the Service Provider.
This forum post suggests using the EmptyStorageFactory strategy for SAML storage and using the defaultTargetURL in SavedRequestAwareAuthenticationSuccessHandler bean.
This post mentions a security risk with this workaround where there is a replay attack possibility. Our SPs and IdPs only HTTPS, so this may be mitigated - are there other vulnerabilities that may introduced?
It could be a problem with your load balancer configuration. In your use case, it is important to configure sticky session in your load balancer. Consider a scenario when authentication request was made by SP1 and your LB redirected the response to SP2, in this case, SP2 going to throw this error that you are receiving. If your LB redirects the response to SP1, login gonna work fine. That can be a random case, and that is why you are not able to consistently reproduce this error. Try configure sticky session in your LB. Let me know if that helps.

Spring Security (basic-authentication) - receiving 401 Unauthorized for correct credentials

I am trying to add a basic authentication security layer over a REST service that I developed using Spring boot 4.3.
I have essentially did two main things:
Implement UserDetailsService interface to provide user details. Here I read from a text file the usernames and the encoded (via Bcrypt) passwords.
I declare a bean as follows in my #EnableWebSecurity annotated security configuration class:
#Bean
public BCryptPasswordEncoder getPasswordEncoder () {
return new BCryptPasswordEncoder();
}
to return the specific password encoder.
I am testing the authentication using rest clients both ARC (for Chrome) and REST Client of Firefox.
The issue is as follows:
When I enter my credentials for the first time and upon successful authentication, the response is 200 OK. This is true for a bunch of users. In other words, as long as I supply correct pair of credentials I get a 200 response.
But, after a single request with incorrect credentials the authentication process breaks down. Sometimes I get 200 and other times a 401 even for correct credentials. Other users authentication is affected as well, at this point of time.
There is a curious log that appears at step 2:
WARN 6813 BCryptPasswordEncoder : Empty encoded password
To elaborate a bit on the hashing of the passwords, I ran the Bcrypt password encoder utility and encoded a bunch of passwords and saved them manually in a text file for the corresponding user names.
I am unsure where the issue lies: whether the clients caching the credentials or the Spring security context caching the incorrect principal or the user details service breaking down (no such exception in the logs though).
Any help is appreciated. If you need any further specific information, please do let me know.

SAML Request - Declare Multiple Protocol Bindings

I have implemented a SAML Service Provider to support Single Sign On for an ASP.Net web portal, which is a shrink-wrap software configured on clients' sites and must be able to interact with any SAML-compliant Identity Provider.
My Assertion Consumer Service (ACS) page will accept the SAML Response through both GET and POST methods.
As I understand the SAML protocol, the SAML Request ProtocolBinding property specifies which protocols are supported for the response. Currently, my request specifies the HTTP-Redirect binding. However, I would like to declare that I support both HTTP-Redirect (GET) and HTTP-POST (POST). After searching through more SAML documentation than I care to reiterate, I am unable to find the syntax for declaring multiple supported protocol bindings (or whether it is even valid to do so).
While I could make this declaration configurable, my preference would be to declare both bindings so that the Identity Provider will work without additional configuration of my portal.
Below is a sample of my Authentication Request. Please, if anyone knows a way to declare both HTTP-Redirect AND HTTP-POST for the ProtocolBinding, I would greatly appreciate your input!
<?xml version="1.0" encoding="utf-8"?>
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="[AUTHN_ID]"
Version="2.0"
IssueInstant="[ISSUE_INSTANT]"
ProtocolBinding="urn:oasis:names.tc:SAML:2.0:bindings:HTTP-Redirect"
ProviderName="[PROVIDER_NAME]"
AssertionConsumerServiceURL="[ACS_URL]">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
PortalEntityID
</saml:Issuer>
<samlp:NameIDPolicy
AllowCreate="true"
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" />
</samlp:AuthnRequest>
Thanks in advance to anyone who can help!
The ProtocolBinding attribute on AuthnRequest is used to specify the expected binding to be used by the IdP when sending their SAML Response XML. HTTP-Redirect isn't a valid option to use here, because of the possible length restriction on the URL querystring; a SAML Response, especially if it's signed, can be pretty lengthy. I'll quote from the SAML spec [SAMLProf]:
...the identity provider issues a <Response> message to be delivered by the user agent to the service provider. Either the HTTP POST or HTTP Artifact binding can be used to transfer the message to the service provider through the user agent. The message may indicate an error or will include (at least) an authentication assertion. The HTTP Redirect binding MUST NOT be used, as the response will typically exceed the URL length permitted by most user agents.
After considerable research, it seems that you can only declare one Protocol Binding in a single SAML request.

Resources