Spring Security - Is SavedRequestAwareAuthenticationSuccessHandler broke? - spring

TLDR: Spring is destroying the current HTTP session when redirecting to the login page; this destroys the ability to navigate to the DefaultSavedRequest after login. Why is this happening?
Details -
I am maintaining a legacy Spring application:
Spring Core version 3.1.0
Spring Security version 3.1.0
When trying to utilize SavedRequestAwareAuthenticationSuccessHandler in my login configuration, it is not working. Here is what seems to be happening:
HTTP GET to secured resource: http://localhost:8080/myapp/viewWorkOrder?workOrderNumber=315261
Spring correctly determines that I am not logged in and saves my request:
DEBUG o.s.s.w.s.HttpSessionRequestCache - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/myapp/viewWorkOrder?workOrderNumber=315261]
Spring correctly redirects to my login page:
DEBUG o.s.security.web.FilterChainProxy - /login.jsp at position 1 of 9 in additional filter chain; firing Filter: 'ChannelProcessingFilter'
Spring destroys the current session which effectively destroys the ability to later use the DefaultSavedRequest:
DEBUG o.s.s.w.s.HttpSessionEventPublisher - Publishing event: org.springframework.security.web.session.HttpSessionDestroyedEvent[source=org.apache.catalina.session.StandardSessionFacade#b25f027]
Why or what is causing the current session to be destroyed?
Here are the pertinent configuration details:
<bean id="savedRequestAwareAuthenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/postLogin" />
<property name="targetUrlParameter" value="targetUrl" />
<property name="alwaysUseDefaultTargetUrl" value="false" />
</bean>
<security:http auto-config="false">
<!-- Override default login and logout pages -->
<security:form-login login-page="/login.jsp"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login.jsp?login_error=1"
authentication-success-handler-ref="savedRequestAwareAuthenticationSuccessHandler"
/>
<security:session-management session-fixation-protection="none"/>
Note that the inclusion of session-management does not seem to affect the feature either way.

Well, this is embarrassing but in the interest of being a good citizen on stack overflow I thought I would share what I found.
After setting a breakpoint in the Spring HttpSessionEventPublisher to see if the stack might give me a clue, it certainly did. Here is a screenshot:
You'll notice that login.jsp is on the stack. Being new to this particular application, I hadn't really even suspected the JSP but here is what I found:
Obviously, removing this scriptlet solved my issue. Now I just wonder why someone did it and what I broke in the process :)

Related

How to prevent session fixation with a Keycloak Spring Security setup?

We use keycloak as our identity and access provider that is connected with spring security by keycloak's spring security adapter. We realized that the session id does not change when a user logs in the application which is an open door for session fixation attacks. Both keycloak and spring security provide solutions for preventing session fixation but when I use both in combination none of them works properly.
From keycloak's documentation:
turn-off-change-session-id-on-login
The session id is changed by default on a successful login on some platforms to plug a security attack vector. Change this to true if you want to turn this off This is OPTIONAL. The default value is false.
I didn't turn off this feature but the session id still remains the same after login procedure.
Spring security comes along with two implementations of the SessionAuthenticationStrategy, ChangeSessionIdAuthenticationStrategy and SessionFixationProtectionStrategy, but none of them does the trick.
In the keycloak doku you can find a hint that "SessionFixationProtectionStrategy is currently not supported" but there is no advice how to deal with the session fixation risk in this setup. But according to this hint it should still be possible to change the session id with spring security with the consequence that "universal log out will not work" anymore. But even this I don't get to work (maybe we could go with the trade-off and lose the universal logout)
I tried changing the session id with spring security on some ways (extraction of the configuration file):
overwrite default session management filter by following this instructions
<http use-expressions="true" auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
[...]
<!-- changeSessionId and newSession have no result at all -->
<session-management session-fixation-protection="none"/>
<session-management session-authentication-strategy-ref="sessionAuthenticationStrategy"/>
<custom-filter ref="sessionManagementFilter" position="SESSION_MANAGEMENT_FILTER"/>
[...]
</http>
<beans:bean id="sessionAuthenticationStrategy"
class="org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy"/>
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="securityContextRepository"/>
<beans:constructor-arg name="sessionStrategy" ref="sessionAuthenticationStrategy"/>
</beans:bean>
Overwrite default session management filter by this instruction
<http ...>
<session-management session-authentication-strategy-ref="sessionStrategy"/>
</http>
<bean id="sessionStrategy" class="org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationSessionStrategy"/>
Changing the behaviour of the default session management filter according to spring's documentation
<http use-expressions="true" auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
[...]
<!-- changeSessionId and newSession have no result at all -->
<session-management session-fixation-protection="changeSessionId"/>
[...]
</http>
Any hints are appreciated about preventing session fixation within a keycloak spring security environment.

Session fixation in Spring Security

We are trying to prevent session fixation attack in our application. This means we are expected to generate new JSESSIONID every time a user logs into application.
Current scenario doesn't generate new JSESSIONID post authentication with ADFS (Active directory). Thus we would like to achieve the same. Can you let us know, how to achieve solution for this kind of attack?
We have Spring, Primefaces and Spring Security used in our application. We tried implementing below tags in our Spring security.xml file. However, it doesnt seem to generate new JSESSIONID post authentication is successful with ADFS. This spring-security.xml has been added in web.xml. Can you let us know what is wrong with below use? We are using Spring Security 3.2.10 in project.
<sec:http create-session="always" use-expressions="true">
<sec:intercept-url pattern="/*" />
<sec:http-basic />
<sec:session-management invalid-session-url="/"
session-fixation-protection="newSession">
<sec:concurrency-control max-sessions="150"
expired-url="/" />
</sec:session-management>
<sec:csrf/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
<sec:user-service>
<sec:user name="abc" password="abc" authorities="ROLE_USER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
If you are using the Basic authentication for API, you'd better not create new session
<sec:http create-session="stateless" ....
If you want to create new session post authentication, the default Basic Filter is not supported, but you can implement your own filter and just like AbstractAuthenticationProcessingFilter, there is SessionAuthenticationStrategy in it, and SessionFixationProtectionStrategy will create a new session with exist attributes in old session post authentication.
I suposse you are using form-login because talking about users login in. Spring includes out-of-the-box session fixation protection. In SessionManagementFilter, in doFilter method, you can see that if the user has been authenticated in the current request, the session authentication strategy is called. This strategy by default is SessionFixationProtectionStrategy.
Apparently your configuration is correct, debug that method and check what is happening. Besides, login forms are recommended to be light and sessionless if possible, so default create-session value "IfRequired" should be preferred instead of "always". Anyway newSession strategy should invalidate current session, ceate a new one and return a new JSESSIONID cookie.

Spring LDAP Context.REFERRAL to follow

How do I set the LDAP Context.REFERRAL to follow in a Spring Security configuration? This is related to a problem I already reported and for which I found an unsatisfactory solution before discovering the real solution I am seeking for involve setting this environment attribute in the LDAP context to follow the referral for the ActiveDirectoryLdapAuthenticationProvider.
Here is the reference to my original question: Spring Security 4.0.0 + ActiveDirectoryLdapAuthenticationProvider + BadCredentialsException PartialResultException
Addendum: It seems no one is having such an environment here. Despite the silence on this problem, I post here my configuration hoping someone will be able to help me with this. I just don't know what I should do to solve this problem.
Here are the error messages in my log:
2015-06-15 10:32:19,810 DEBUG (o.s.s.w.FilterChainProxy$VirtualFilterChain.doFilter) [http-8443-1] /identite.proc at position 7 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
2015-06-15 10:32:19,810 DEBUG (o.s.s.w.u.m.AntPathRequestMatcher.matches) [http-8443-1] Checking match of request : '/identite.proc'; against '/identite.proc'
2015-06-15 10:32:19,810 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.doFilter) [http-8443-1] Request is to process authentication
2015-06-15 10:32:19,811 DEBUG (o.s.s.a.ProviderManager.authenticate) [http-8443-1] Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
2015-06-15 10:32:19,811 DEBUG (o.s.s.l.a.AbstractLdapAuthenticationProvider.authenticate) [http-8443-1] Processing authentication request for user: myusername
2015-06-15 10:32:19,841 DEBUG (o.s.s.l.SpringSecurityLdapTemplate.searchForSingleEntryInternal) [http-8443-1] Searching for entry under DN '', base = 'dc=dept,dc=company,dc=com', filter = '(&(userPrincipalName={0})(objectClass=user)(objectCategory=inetOrgPerson))'
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Updated SecurityContextHolder to contain null Authentication
2015-06-15 10:32:19,842 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-1] Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#a5d7f2
And here is the configuration:
<b:bean id="monFournisseurAD" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<b:constructor-arg value="dept.company.com" />
<b:constructor-arg value="ldap://dept.company.com:3268/" />
<b:constructor-arg value="dc=dept,dc=company,dc=com" />
<b:property name="searchFilter" value="(&(userPrincipalName={0})(objectClass=user)(objectCategory=inetOrgPerson))" />
<b:property name="userDetailsContextMapper">
<b:bean class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />
</b:property>
<b:property name="authoritiesMapper" ref="grantedAuthoritiesMapper" />
<b:property name="convertSubErrorCodesToExceptions" value="true" />
</b:bean>
<b:bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<b:constructor-arg value="ldap://dept.company.com:3268/dc=dept,dc=company,dc=com" />
<b:property name="baseEnvironmentProperties">
<b:map>
<b:entry key="java.naming.referral" value="follow" />
</b:map>
</b:property>
</b:bean>
<b:bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<b:constructor-arg ref="contextSource" />
</b:bean>
<b:bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />
<b:bean id="myDeconnexionHandler" class="com.company.dept.web.my.DeconnexionHandler" />
The two beans with ids contextSource and ldapTemplate seems not to change anything. I was trying to get the referral follow behavior. It seems not the right way to configure it. Also, here the port in the ldap URL is set to 3268 because it is the general catalog and someone in another description elsewhere suggested to use it. But, the results are exactly the same with port 389.
If I change the first constructor argument in the bean monFournisseurAD to set it to a single userPrincipalName domain, it will work for all users into that domain. While I can actually authenticate anyone from the command line using ldapsearch command using dept.company.com instead of the userPrincipalName domain associated directly to the user. In fact, if I enter a wrong password, I will get the specific wrong password message. This seems to prove the user is actually authenticated by AD/LDAP, however Spring fails later to fetch the attributes for that user.
How can I solve this problem?
Finally, the only way to solve this problem is to modify the code since the method org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.searchForUser() is wired to pass the bindPrincipal to SpringSecurityLdapTemplate.searchForSingleEntryInternal() which actually recover the record for the user using the search filter defined in the XML or set with setSearchFilter(). Since the bindPrincipal is actually the username#domain, this value is not suitable for sAMAccountName usage in the search filter, it will always fail. Since the bind to the Active Directory server is performed using the userPrincipalName and then the bindPrincipal you have no way to specify to searchForUser() it must use the username only in the search rather than the bindPrincipal (UPN). I fixed my problem by copying the whole class org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider in my package and modify the searchForUser() method to pass the username instead of the bindPrincipal to the method searchForSingleEntryInternal(). This works, but it is still a wired/hardcoded solution. A more elegant solution would be to introduce a list of patterns and a method to compose the values for the call to searchForSingleEntryInternal() or a method that would detect the kind of arguments required from the searchFilter() string.

Spring Security custom filter registration with Java config

I am trying to setup pre-authentication authorisation using Spring Security, similar to site minder where an external system does the authentication and saves login information in a cookie. However for that to happen I need to redirect to the external URL.
I tried doing it from an implementation of AbstractPreAuthenticatedProcessingFilter but that doesn't work because the HttpServletResponse object is not available.
A more appropriate way seems to be to just add a custom filter that checks for cookie and does the redirection and once the cookies are available then passes the control forward to Spring Security filter. How can I register this custom filter in a Java configuration based Spring Security application? Any help would be appreciated.
The common way is to redirect user to the external authentication interface using an AuthenticationEntryPoint, for example LoginUrlAuthenticationEntryPoint. The entry point is automatically invoked by Spring Security whenever it determines that user needs to get authenticated.
Once user returns back to your application, it should hit a custom filter which extends the AbstractPreAuthenticatedProcessingFilter and extracts the username from your cookie/header/token (after perhaps some validity and integrity checks) in method getPreAuthenticatedPrincipal.
The Spring configuration could be similar to:
<security:http entry-point-ref="externalAuthentication">
<security:custom-filter after="BASIC_AUTH_FILTER" ref="cookieAuthentication"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
</security:http>
<bean id="externalAuthentication" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="http://server.local/authenticationService"/>
</bean>
<bean id="cookieAuthentication" class="custom class extending org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter">
...
</bean>

how to configure session time out in spring acegi framework?

We are using acegi security for my spring application.
Can you please help how to make user to session time out by 5 mins and go back to login screen?
I tried to configure session-timeout in web.xml. But it is not working.
Thank you for your help and time.
I realized that i need to keep 5 min = 300000 mill secconds on expiring tickets in acegi security configuration xml.
Now i have another questions that how to redirect to application home page on login. Currently it is trying to go to the page where it previously logged out. But i want to make it as home page on what ever condition.
Your help is greatly appreciated. Thank you.
to get forwared to a specific url after a timeout (defined in the web.xml), you may use
<http>
...
<session-management invalid-session-url="/sessionTimeout.htm" />
</http>
Session Management docu
Changing following expiration policy, we can make session log out within time
<bean
id="serviceTicketExpirationPolicy"
class="org.jasig.cas.ticket.support.MultiTimeUseOrTimeoutExpirationPolicy">
<constructor-arg
index="0"
value="1" />
<constructor-arg
index="1"
value="600000" />
</bean>
Cas expiration policies gives more information.
To redirect to login page use following:
<property name="alwaysUseDefaultTargetUrl" value="true"/>
in casProcessingFilter bean configuration

Resources