Stack trace when using Spring Security - spring-security

I'm using Spring Security for user authentication in my web app and I noticed this in my stack trace:
[DEBUG] [http-bio-8080-exec-3 10:48:02] (ExceptionTranslationFilter.java:handleSpringSecurityException:165) Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
etc...
Is this normal behavior? I now it's not an error, but it looks similar :)

It is a debug message that tell you spring is redirecting to login page because of an anonymous access to a restricted area.
You can change the log level for org.springframework.security packege if you want, but this is a normal behaviour

Yes, this is normal; note that this is not an error, only a debug.
This is how Spring's filter-chain works - when one filter fails, it returns to the next chain. In this case it is the ExceptionTranslationFilter that fails, and now the authentication filter (UsernamePasswordFilter?) attempts to authenticate the user.

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.

Handling org.jasig.cas.client.validation.TicketValidationException

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);

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.

Redirect after Session timeout (Grails, Spring Security Core, Tomcat)

I have an application developed in Grails (v1.3.7) and we used Spring Security Core (v1.2.6) plugin for authentication. After building .war file, I have deployed the application in a standard tomcat server (v7.0.22). The application runs fine.
I know that I can configure Session timeout period in web.xml either before building the application or in the tomcat server itself. But, I want (additionally) to redirect any page to the log-in page automatically whenever the Session is timed out. Because, if the Session times out and users click on any links or simply refresh the current page, they get a tomcat error.
Can anyone suggest a way to resolve it easily? Is there any configuration (like expired-session-url) in Tomcat or Spring Security Core that does the job?
I have search in the plugin doc site, plugin blog site but nothing found. This site suggest that I would require to add a listener in code and I would hate to do that and would like to use a simple configuration like this. Can anyone guide anything?
Thanks in anticipation
Http is stateless protocol, and session is just a marker stored on client cookies (+ local db), and you can't handle this as an event. 'new client' and 'session expired' is the exactly same, it just means that you can't identify browser for current request. For most cases it means also that user is not authenticated (for raw Spring Security Core, at least)
For you case, you already have session expired handler, it's when you're getting this tomcat error. Just handle this error, and redirect user to login page.
Btw, if you have proper Spring Security configuration, it must redirect all non-authorized users to login page. And seems that you have made something wrong with your app architecture, if you have authenticated user, but still having some user datails in standard tomcat session. There at least two ways: avoid your own user session, or make some kind of session-based Spring Security authentication config.

What exception should I inherit from or throw and how does this affect the HTTP error code?

What .NET exception should be thrown when a user doesn't have access (Unauthorized) to a particular record in a database?
There are two separate logic cases I'd like to handle:
Where the user is anonymous, and they should log in to get additional rights. If this happens, I want to redirect them to
Where the user is logged in, but the account they are using doesn't have enough rights. In this case, I want to offer the ability to request access.
What HTTP error should I use? Should I issue a 401 or a 403?
Generally speaking, how should I implement this in ASP .NET MVC?

Resources