I need to customize my authentication process in such manner:
Client sends request (REST API) with a "special" URL param
Server calls third-party service passing a param and receiving user name
Server lookups database by name and this is authenticated principal.
I split my server side (2+3) on two parts - custom filter for (2), that obtains user name - and a custom userdetailservice for(3) that builds principal by looking up name in database.
But I cannot build my security.xml correctly - every time it seems that it doesn't process filter at all. I think the problem is in the first (http) node, but I cannot understand what position should I set up for filter. Here is my config:
<http use-expressions="true" auto-config="true" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/*" access="isAuthenticated" />
<custom-filter ref="casServiceTicketFilter" position="FIRST"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="wliAuthenticationService"/>
</authentication-manager>
<b:bean id="casServiceTicketFilter" class="org.WLICASAuthenticationFilter">
<b:property name="casTicketValidateURL" value="${cas.ticket.validate.url}"/>
<b:property name="authenticationManager" ref="authenticationManager"/>
</b:bean>
<b:bean id="wliAuthenticationService" class="org.WLIUserDetailService"/>
PS- Please don't tell me that Spring has CAS support out-of-the-box. It's a bit various configuration so I need to create my own implementation of service ticket validator
Your custom authentication filter shouldn't be first in the filter chain. It needs to come after the SecurityContextPersistenceFilter. Use
<custom-filter ref="casServiceTicketFilter" after="SECURITY_CONTEXT_FILTER"/>
instead.
If you enable debug logging, you should be able to see clearly what order the filters are called in for each request and whether yours is invoked.
Related
I am working on a system that uses Spring 4.3.25.RELEASE and xml based configuration. I need to integrate with another system using OAuth2, and therefore trying to configure the system as an OAuth2 Client, but it's proving difficult to find examples and documentation.
I can redirect to the IdP ok, but on return I am seeing this error:
Possible CSRF detected - state parameter was required but no state
could be found
This is the configuration I have in place, which is obviously incomplete. Can you please help me identify what is missing?
Thanks.
<custom-filter ref="oauth2ClientFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
<custom-filter ref="oauth2AuthenticationFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
...
<oauth:client id="oauth2ClientFilter" />
<beans:bean id="oauth2AuthenticationFilter" class="org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter">
<beans:constructor-arg name="defaultFilterProcessesUrl" value="/oauth2/callback"/>
<beans:property name="restTemplate" ref="restTemplate"/>
</beans:bean>
<oauth:rest-template id="restTemplate" resource="oauth2Token"/>
<oauth:resource id="oauth2Token"
type="authorization_code"
client-id="my-client-id"
client-secret="my-client-secret"
access-token-uri="https://http://myurl/token"
user-authorization-uri="http://myurl/authorize"/>
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.
I have implemented resource owner flow with spring oauth2 based on spring's sparklr sample application and a couple of samples I found online. I tested the token request part with curl like this in order to provide both client and user credentials:
curl -v --data "username=user1&password=user1&client_id=client1&client_secret=client1&grant_type=password" -X POST "http://localhost:8080/samplerestspringoauth2/oauth/token"
and it works correctly, however I have made the following observation:
Although according to the examples I saw, I make use of the BasicAuthentication filter, this is not really used in the security process. Since the token request does not contain an Authentication header, the BasicAuthentication filter just skips doing any checks. ClientCredentialsTokenEndpointFilter and authentication-server are the only ones performing security checks during the token request. After noticing this and verifying it via debugging, I tried to remove completely the following part:
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
from the configuration. But then I got the warning:
"No AuthenticationEntryPoint could be established. Please make sure
you have a login mechanism configured through the namespace (such as
form-login) or specify a custom AuthenticationEntryPoint with the
'entry-point-ref' attribute".
As a next step, I added the entry-point-ref="clientAuthenticationEntryPoint in the http namespace, and got rid of the warning. I tested the app and played correctly.
However, in addition to the above, I have also made the following observation during debugging:
The ClientCredentialsTokenEndpointFilter, contains its own OAuth2AuthenticationEntryPoint entry point inside a private variable, and uses that when failing due to wrong client credentials.
Therefore, it does not matter what entry point I specify either in the basic filter, or in the http namespace. At the end ClientCredentialsTokenEndpointFilter will use its own private OAuth2AuthenticationEntryPoint.
To summarize my conclusions seem to be the following:
The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
Specifying either a basic
filter,or an endpoint in http namespace is needed only for the
compiler to stop the warning. They have no practical use, and the
endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
Below I put the http and endpoint configuration for the token request for your reference. I skip the rest of configuration for keeping the post easy to read:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
I also assume that the same issue also occurs in the original sparklr application (which is spring oauth2 sample app) configuration for token request which is very similar. That can be found in https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml, and the related part is below:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/**" method="GET" access="ROLE_DENY" />
<intercept-url pattern="/**" method="PUT" access="ROLE_DENY" />
<intercept-url pattern="/**" method="DELETE" access="ROLE_DENY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
I would expect spring oauth2 to more appropriately interact with spring security instead of having to put unnecessary and misleading configuration, and that makes me think that I may have missed something. Since security is a sensitive aspect I wanted to share that with you and ask if my conclusion correct.
The /oauth/token provides two different ways to authenticate clients which are requesting tokens:
Using HTTP-Basic authentication (when "http-basic" element is present)
The authentication is handled with org.springframework.security.web.authentication.www.BasicAuthenticationFilter and processes the "Authorization" HTTP header which contains base64 encoded credentials of the client. The filter only performs processing when the Authorization header is present. This method is always tried first. The entry point defined on http-basic will only be invoked when user has supplied an "Authorization" header with invalid content - that's why you don't see the entry point invoked in your debugger, try to set an Authorization HTTP header and your breakpoint will get a hit.
As defined in the OAuth standard using client_id and client_secret HTTP paremeters
This is handled using org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter and by default uses entry point which sends back WWW-Authenticate header to the client. The default entry point can be customized (there's a setAuthenticationEntryPoint method). The entry point is only used when you supply client_id parameter.
Both of these methods use different ways to obtain client's username+password, but verify it against the same authentication manager.
The "No AuthenticationEntryPoint could be established" error which you observe when taking out the <http-basic> element is coming from Spring Security itself, not from the OAuth Extension. The reason is that Spring Security is not able to tell that there's a default entry point already configured inside of the custom filter ClientCredentialsTokenEndpointFilter. And the HTTP configuration of Spring Security always must have at least one entry point available.
So, the complete logic goes as follows:
when you include "Authorization" header with invalid credentials and <http-basic> element is present , system will use entry point defined on the <http-basic> element. If none is specified (attribute entry-point-ref is missing), system will create a default instance of BasicAuthenticationEntryPoint automatically for you and use it.
when you include HTTP parameter "client_id" and "client_secret" with invalid credentials and custom filter clientCredentialsTokenEndpointFilter is present, system will use entry point defined in the clientCredentialsTokenEndpointFilter bean (which is by default instance of OAuth2AuthenticationEntryPoint)
in case neither "Authorization" header nor "client_id" parameter are present and the endpoint requires authentication ("IS_AUTHENTICATED_FULLY"), system will use the entry point defined on the <http entry-point-ref="">, if present, otherwise it will use the entry point defined on the http-basic (as above)
in case you don't specify neither http-basic (or other default authentication method which Spring recognizes), nor default entry point using the <http entry-point-ref="">, system will fail with "No AuthenticationEntryPoint could be established", because it requires at least one entry point and it doesn't understand that there's one available inside the clientCredentialsTokenEndpointFilter.
Regarding your observations:
>> The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
> This is true in case you are authentication your clients using client_id + client_secret
>> Specifying either a basic filter,or an endpoint in http namespace is
needed only for the compiler to stop the warning. They have no
practical use, and the endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
> Partly true, as the entry point will be used in case client_id is missing.
The configuration is indeed confusing (which is partly caused by the fact that OAuth isn't a native part of Spring Security, but an extension), but all of those settings make sense and are used in specific situations.
The changes you made have no security implications.
I want to integrate LDAP in my spring application.
Requirement:- On request it should divert to my login page then ask for user/password. Then on submit it should authentication from LDAP.
Thanks
There is a special project in Spring called Spring Security for this purpose. The core functionality is built as a set of servlet API filters. There are multiple connectors for user's database (LDAP, DB, Active Directory, etc.) Here you can see how to add a basic conf. Your conf may looks like this:
<http use-expressions="true">
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login />
<logout />
</http>
Note that I prefer SpEL expressions for security rules. And here you can see how to add LDAP.
Hope it helps.
Along with that you also need other LDAP configuration like this
<ldap-server url="ldap://localhost:10389/dc=example,dc=com" />
<authentication-manager alias="authenticationManager"
erase-credentials="true">
<ldap-authentication-provider
user-dn-pattern="uid={0},ou=people" group-search-base="ou=groups"
group-search-filter="(members={0})">
</ldap-authentication-provider>
</authentication-manager>
I have my Spring 3.1 app configured like this
<http use-expressions="true" entry-point-ref="http401UnauthorizedEntryPoint">
<intercept-url pattern="/app/demo" access="hasRole('Demo')" />
<intercept-url pattern="/app/**" access="isAuthenticated()" />
<intercept-url pattern="/admin/**" access="hasRole('Admin')" />
<custom-filter position="PRE_AUTH_FILTER"
ref="currentWindowsIdentityAuthenticationFilter" />
<logout invalidate-session="true" delete-cookies="JSESSIONID"
logout-url="/logout" logout-success-url="/logout-success" />
</http>
I have written a custom preauth filter. When I call my app at the root URL / the filter chain hooks in and runs the preauth filter although this resouce is not protected. This means that the logout does not work as designed. After a logout a login is performed again.
My implementation is based on the org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter class.
Is this normal behavior or can this be fixed some how? I'd like the auth be performed on the protected URLs only.
As I side note, I do not intend to configure security='none' because I want to maintain the security context on all pages.
I have posted the appropriate log out on pastebin. It is too verbose to include in here.
Seems that what you want is creating special <http> without any filters for logout URL:
<http pattern="/logout/**" security="none" />
<http use-expressions="true" entry-point-ref="http401UnauthorizedEntryPoint">
<intercept-url pattern="/app/demo" access="hasRole('Demo')" />
<intercept-url pattern="/app/**" access="isAuthenticated()" />
<intercept-url pattern="/admin/**" access="hasRole('Admin')" />
<custom-filter position="PRE_AUTH_FILTER"
ref="currentWindowsIdentityAuthenticationFilter" />
<logout invalidate-session="true" delete-cookies="JSESSIONID"
logout-url="/logout" logout-success-url="/logout-success" />
</http>
Read more about request matching mechanism here.
EDIT:
#LukeTaylor mentioned that if you want to create another filter chain then the pattern should go in the element (is this documented somewhere explicitely?), so my idea with separate chain without PRE_AUTH_FILTER obviously won't work. Added <http> for /logout without any filters, which should prevent authorizing at logout requestes.
Still, I don't know how prevent requests like /other from applying PRE_AUTH_FILTER. One way could probably be abandon <http> namespace configuration to manual filterChainProxy configuration with two <sec:filter-chain> patterns, but I don't know if it's worth it.
#Michael-O: About exception IllegalArgumentException: A universal match pattern ('/**') is defined before other patterns - it's strange, is it your whole XML config for Security? Or maybe it's just a consequence of what Luke said (that another <http> element should have pattern)...
I was able to indentify the issue but it cannot be solved the way it is now because of the way the entire chain works.
Here's the deal:
When you define a <http> element on /** you ask Spring Security to fire the entire filter chain on all paths under your defined pattern. It does not matter whether one of them needs protection or not. Rob Winch published a very helpful video. If you take a closer look at the default filter stack you'll what filters are applied. Amidst these is my filter located.
The first ten lines of my log file reveal that the entire chain is fired since / matches the <http> configuration. At the end, the FilterSecurityInterceptor sees that this resource does not need protection. More over, you see that the CurrentWindowsIdentityAuthenticationFilter is fired too and performs unwanted authentication.
Why? Compared to header-based filters or URL processing filters you have no trigger/entry point to commence the authentication deliberately you simply do without challenging the client regardless the URL needs protection or not. Defining something like this <http pattern="/unprotected-url" security="none" /> saves you absolutely nothing because you lose the security context on unprotected paths. You want to keep your client logged in regardless of the URL protection.
How can this be solved now? You have two options:
Define a <http> element on /app/**, /admin/** so on but this is really cumbersome and contains repitions all over. I would not recommend such a solution. Additionally, you probably won't have the sec context on other URLs in /**. This is not desired.
Split the preauth filter in two filters:
CurrentWindowsIdentityPreAuthenticationFilter
CurrentWindowsIdentityUrlAuthenticationFilter
The second option solves the problem.
CurrentWindowsIdentityPreAuthenticationFilter: Remains as-is and peforms the auth always. Very helpful for M2M communication like script access or REST requests.
CurrentWindowsIdentityUrlAuthenticationFilter: Suits human interaction very well. It works basically like a form-based filter. If define a URL, say /login, you will get redirected to when you request a protected resource and after successful auto-auth you be redirected back to your actual resource. Auth is done. Public resources remain unauthenticated because the preauth filter is trigged on /login only just like form-based. If you log out you stay logged out.
I'd be happy if any of the Spring folks can confirm my analysis.