Spring 4 Security configuration runs but not active - java

I am quite new to Spring, so sorry if my question sounds silly.
I am trying to add basic HTTP Auth to my Spring based REST API.
I have been following quite a few tutorials and reference documentations for now, but they all seem to indicate the same thing so it seems obvious I must be missing something.
What I want is to define a simple configuration where every request is behind HTTP Auth by default, but then we can define method level security as needed using #PreAuthorize, or by modifying the HttpSecurity configuration.
I have defined a very simple configuration as such:
#Configuration
#EnableWebMvc
#EnableWebSecurity
#ComponentScan(basePackages = "com.app.rest")
public class RESTConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().hasAnyRole("USER")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
I then have a very simple controller (no service, nothing fancy).
Here is a very basic overview:
#RestController
#RequestMapping("/images")
public class ImagesController {
#JsonView(View.Basic.class)
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ObjectXvo getImageByID(#PathVariable String id) throws IOException {
ObjectXvo result = doThings();
return result;
}
}
The only noticeable difference compared to all the online tutorials is the way we load this API. Because we already have a Jetty container running other things, we have decided to reuse it instead of using a WebAppInitializer like most do online.
Here is an extract of how the REST API is defined:
// Creating REST Servlet
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setErrorHandler(null);
restHandler.setContextPath("/rest");
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.app.test");
WebApplicationContext webAppContext = context;
DispatcherServlet dispatcherServlet = new DispatcherServlet(webAppContext);
ServletHolder springServletHolder = new ServletHolder("REST dispatcher", dispatcherServlet);
restHandler.addServlet(springServletHolder, "/");
restHandler.addEventListener(new ContextLoaderListener(webAppContext));
// Creating handler collection
ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
handlerCollection.addHandler(anotherHandler);
handlerCollection.addHandler(restHandler);
handlerCollection.addHandler(yetAnotherHandler);
// Setting server context
server.setHandler(handlerCollection);
The thing is, when running the app, is that I can still access all URLs like if I had not setup any security scheme.
When debugging the application, I can see that the RESTConfiguration is being processed as breakpoints inside my configure methods are definitely being hit.
We are quite set on avoiding the usage of XML files as long as we can, as we think annotations are better so far.
Can anyone point me in the right direction as to why this security configuration is not being activated?
EDIT:
Not sure how relevant that is, but if I try to add a #PreAuthorize to a REST method, I get the following error:
HTTP ERROR: 500
Problem accessing /rest/images/I147. Reason:
An Authentication object was not found in the SecurityContext
There is plenty of info on the internet on how to fix this, but I am wondering if this is not related.

You have to register springSecurityFilterChain, see Spring Security Reference:
The next step is to register the springSecurityFilterChain with the war. This can be done in Java Configuration with Spring’s WebApplicationInitializer support in a Servlet 3.0+ environment. Not suprisingly, Spring Security provides a base class AbstractSecurityWebApplicationInitializer that will ensure the springSecurityFilterChain gets registered for you. The way in which we use AbstractSecurityWebApplicationInitializer differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application.
If you don't use AbstractSecurityWebApplicationInitializer you have to register springSecurityFilterChain manually:
webAppContext.getServletContext()
.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC), false, "/*");
See also:
ServletContext#addFilter
FilterRegistration#addMappingForUrlPatterns

Related

Spring Boot HTTP security configuration anonymous filter and a custom filter on a different path

I have experienced a strange problem while trying to configure HTTP security by using WebSecurityConfigurerAdapter. Here is the full configuration class I tried so far:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ConditionalOnProperty(name = "module.client.security.enabled")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${management.endpoints.web.base-path}")
private String managementEndpoint;
#Autowired
private List<ModuleProperties> moduleProperties;
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(this.managementEndpoint + "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.formLogin().disable();
// FIXME also doesn't work because of the later http.requestMatchers().antMatchers() calls
http.authorizeRequests()
.antMatchers("/**").anonymous();
http.requestMatchers()
.antMatchers("/app/**")
.and()
.addFilterBefore(new ClientResolveFilter(), FilterSecurityInterceptor.class);
}
What I would like to do is actually enabling anonymous authentication for all endpoints — to prevent NullPointerExceptions when operating on SecurityContextHolder — Plus, enabling/adding a custom filter to only a subset or different endpoint path which is /app/** in this case.
I expected the above code would work but what exactly happens is the AnonymousAuthenticationFilter disabled for all and both filters only work for the path /app/** only.
If I remove the http.requestMatchers().antMatchers("/app/**") part, then AnonymousAuthenticationFilter works for all paths as normal. I suspect that the second .antMatchers("/app/**") call kinda replaces the former one or substitutes it implicitly which doesn't make sense to me, but I could be wrong.
I tried diving into the source but still confused and cannot find a clean solution to make it work as my expectation. Any ideas and help will be appreciated.
Cheers!
EDIT: I'm using Spring Boot 2.5.2 and the Spring Security version is 5.5.1
The addFilterBefore (and other addFilter*) method will add the filter to the filter chain which apply to all request. If you want the filter to apply to certain requests only, then you have to check inside the filter (eg. using HttpServletRequest.getgetRequestURI() to check the url).
After #Dickson's advice, I found a special bean called FilterRegistrationBean provided by spring boot.
Thus, I configured it as a bean which applies a specific servlet filter to only configured paths:
#Bean
public FilterRegistrationBean<ClientResolveFilter> clientResolveFilter(){
final FilterRegistrationBean<ClientResolveFilter> frb = new FilterRegistrationBean<>();
frb.setFilter(new ClientResolveFilter());
frb.addUrlPatterns("/app/*");
return frb;
}
This solution worked perfectly for my requirement.
Pay attention to that the path string is not an ant matcher now — must be written with single /app/* instead of double /app/** — it's actually the pattern when we manually configured web.xml file like in the old days :)

Whitelist for redirect URLs in spring boot

We have a security issue in our project. An attacker can intercept a login request and modify a 'Host' header in it. The server would respond with a redirect (303), sending user to a possibly evil site.
Is it possible to add a whitelist for redirects?
Using Spring-boot with embedded tomcat, in production this whole thing will be behind a load balancer.
#Override
protected void configure(HttpSecurity http) throws Exception {
http //#formatter:off
.formLogin()
.loginProcessingUrl("...")
.usernameParameter("...")
.passwordParameter("...")
.loginPage("/").permitAll()
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.and().logout().permitAll()
.logoutSuccessHandler(logoutSuccessHandler)
.deleteCookies(XSRF_TOKEN, JSESSIONID)
.and().authorizeRequests()
.antMatchers("...").permitAll().anyRequest().authenticated()
.and().csrf().csrfTokenRepository(csrfTokenRepository)
.and().addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);//#formatter:on
}
Until now I have tried the following:
to use TomcatEmbeddedServletContainerFactory and adding a Valve there.
to use FilterRegistrationBean and using a RemoteAddrFilter.setDeny().
The 1° option wouldn't start at all. I'm obviously making mistakes somewhere, but this information is hard to find and I don't have the complete picture in my head of what to do.
The 2° option I found here on Stackoverflow and it feels the right thing to do, but I failed to make it work. If setDeny() is present, it wouldn't even let me enter my web-site. If I comment it out, then looks like no filtering happen at all. The bean looks like this:
#Bean
public FilterRegistrationBean remoteAddressFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
RemoteAddrFilter filter = new RemoteAddrFilter();
filter.setDeny("attacker/.com.*");
filter.setDenyStatus(404);
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
Thanks in advance for any kind of help.
This is an Old question. But adding an answer as Spring has added support for whitelisting in Spring security 4.2.17 and 5.2. This might be useful for others who stumble on same thing In security config,
#Override
public void configure(WebSecurity web) throws Exception {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHostnames(Arrays.asList("myhostname1","myhostname2"));
web.httpFirewall(firewall);
}
It will throw org.springframework.security.web.firewall.RequestRejectedException
with message like "The request was rejected because the domain www.attackersite.com is untrusted."
If you don't want all the features of StrictHttpFirewall, you can extend HttpFirewall and add your own implementation.

How to register AuthenticationSuccessHandler or SAMLRelayStateSuccessHandler in Spring Security's SAML extension?

tldr; What's the appropriate way to register either a custom AuthenticationSuccessHandler or configure the SAMLRelayStateSuccessHandler to set the redirect URL Spring Security's SAML extension after a successful authentication?
I am new to Spring and Spring Security, but I've had some success getting the SAML extension to work for a Single Sign-On application. However, I've run into a problem where Okta (the IDP) and my application will loop continuously after successful authentication instead of going to the original URL. My suspicion is that this is caused by the fact that I am using Vaadin as well as Spring and once the Vaadin servlet loads it is doing something funky with or ignoring the Spring filters entirely. This is suggested a possible problem and solution in a blog article on combining filter-based Spring Security with Vaadin where the proposed solution is to register an AuthenticationSuccessHandler.
However, this is not as simple with the Spring Security SAML Extension as it is with, say, a simple form login or OpenID. For example, with a form login registing a success handler is as easy as
http.formLogin().successHandler(handler);
No such option is available with the SAML Extension. However, in another post Vladimír Schäfer (see "Spring saml - how remember request parameter when initiate login on SP, and procesing them after IdP response) suggests changing the AuthenticationSuccessHandler to a SAMLRelayStateSuccessHandler. The documentation suggests that this would be the perfect way to do it. However, I would prefer not to subclass SAMLEntryPoint.
My current SecurityConfiguration is entirely Java-based and follows Matt Raible's article on using Spring and Okta together. The SecurityConfiguration is as follows:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable() // Disable Spring's CSRF protection and use Vaadin's.
.authorizeRequests() // Everything else: Do SAML SSO
// Allow everyone to attempt to login
.antMatchers("/root/saml*").permitAll()
// But make sure all other requests are authenticated
.anyRequest().authenticated()
.and()
// Create a completely new session
.sessionManagement().sessionFixation().newSession()
.and()
// Configure the saml properties
.apply(saml())
.serviceProvider()
.keyStore()
.storeFilePath("saml/keystore.jks")
.password(this.password)
.keyname(this.keyAlias)
.keyPassword(this.password)
.and()
.protocol("https")
// Do I need to adjust this localhost piece?
.hostname(String.format("%s:%s", "localhost", this.port))
.basePath("/")
.and()
// Load the metadata from the stored properties
.identityProvider().metadataFilePath(this.metadataUrl);
}
```
What's the best way to register the AuthenticationSuccessHandler or configure the SAMLRelayStateSuccessHandler? Any other thoughts would be greatly appreciated too!
I'm using:
#Bean
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
super.onAuthenticationSuccess(request, response, authentication);
}
};
successRedirectHandler.setDefaultTargetUrl("/");
return successRedirectHandler;
}
It seems to avoid needing a separate securityContext.xml file.
The solution to registering a custom handler is to configure the Spring SAML extension using the sample securityContext.xml file provided in the Sample program. Full details are available by carefully reading the Spring SAML Extension guide. If you are using Spring Boot, you need to tell it to import the securityContext.xml file by adding an #ImportResource annotation. In my case, I removed all the code from my security configuration class, but left the annotations and added the #ImportResource as follows:
/**
* This class is the main security configuration for working with the
* Single-Sign On SAML backend. All it does it import the security context file
* and configure other annotation-based options. All of the real configuration
* options are in WEB-INF/securityContext.xml.
*
* #author Jay Jay Billings
*
*/
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#ImportResource("WEB-INF/securityContext.xml")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {}
Once the securityContext.xml file is in place and reconfigure for your application, you can add a custom handler in the successRedirectHandler bean:
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="com.example.app.customSuccessHandler">
</bean>
That is the answer to my question, but it did not fix my redirect loop problem on its own. The solution to the redirect loop also required 1) fixing the entity base URL in the MetadataGenerator bean, and 2) fixing the security:intercept-url pattern value to account for my modified (non-root) base URL. For information on the latter, see "This webpage has a redirect loop in spring-security application" on this site.

Spring Security: How to reject a request by default if no #PreAuthorize was specified

I have a simple Spring Boot application which exposes a REST API.
I have successfully configured the spring security to secure every method in the rest API according to its ROLE, using the #PreAuthorize("hasRole('ROLE_4')") annotation.
I have noticed that If I don't put the #PreAuthorize annotation at all, the framework allows this request to any authenticated user. I want to reverse this behavior. So if one of the programmers will forget to add the #PreAuthorize annotation, any request to this method will be rejected automatically.
Here is my configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//Disable HTTP Basic authentication
http.httpBasic().disable();
//Add the filters
http.addFilterBefore(new AuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(securityServiceAuthenticationProvider());
}
#Bean
public AuthenticationProvider securityServiceAuthenticationProvider() {
return new SecurityServiceAuthenticationProvider();
}
}
Thanks
Guy Hudara
You can use a MethodSecurityInterceptor; sample XML configuration is here. The example applies security to a single bean, but the expressions are very flexible, you can protect e.g. all public members of any class with name ending in "Controller". I have used a similar XML configuration before, but I haven't done this with Java configuration; but I suppose you can do the same thing in Java configuration.
You could specify a custom AccessDecisionManager and, if the queried object is a MethodInvocation, then check if it has a #PreAuthorize annotation; if yes, let it pass. Otherwise, fail it. You can add it to the configuration like this: http.authorizeRequests().accessDecisionManager(myManager).

Develop Spring Security as a Common Module and Swicting it in Spring Boot Micro Service Architecture

I have a micro service architecture with spring boot. I decided to add Spring security for each micro service which will authenticate, authorise the user.
So i develop a separate project with has Spring Security authentication.
I have use a Filter which extends AbstractAuthenticationProcessingFilter.
The paths which needs authentication and authorisation are mentioned in my filter class as below,
private AntPathRequestMatcher[] authenticationMatcher = {
new AntPathRequestMatcher("//api/myservice1/**"),
new AntPathRequestMatcher("/api/myservice")
};
private AntPathRequestMatcher[] authorizationMatcher = {
new AntPathRequestMatcher("/api/myservice")
};
So in the filter class doFilter method i check request path and do relevant logics.
My SecurityConfig class configure method just look like below,
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(getMyAuthenticationFilter(), BasicAuthenticationFilter.class);
}
So my questions are,
What approach i should do for introduce this module (project) to each micro service?
What i had in my mind is expose this as a jar file and use it in any micro service. In that case how can i over ride those authenticationMatcher and authorizationMatcher url's which will be specific to each micro services?
Am i declare those url's in correct place and if so what Object Oriented principles i should apply?
Is there a possibility i can by pass authentication filter if required and enable it when required? Like switching it?
I believe this approach can work like you want and can be done using Spring Boot. In regards to your questions:
In your filter class you can declare something like this which can be populated by bean initialization.
#Value("${security.checkValues}")
private String[] checkValues;
Below is an example I used with my own custom filter declared as a bean and passed in to the security configuration. You probably don't need all of it.
#Configuration
#Order(3)
public static class SubscriptionWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/subscribe**","/subscribe/**").and()
.addFilterBefore(applicationSecurityTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
#Bean
public ApplicationSecurityTokenFilter applicationSecurityTokenFilter() {
return new ApplicationSecurityTokenFilter();
}
}
Then each application.properties instance in your micro services (assuming they are separate projects) will have a line like below to specify the URLs to use.
security.checkValues=/api/myservice,/api/myservice2
If you include an empty string property like this:
security.checkValues=
Then the String array will be set to a 0 length array and you can know that it should not be active. I'm not entirely sure this is what your question was referencing so please review.
Let me know if this is what you are looking for. We can flush it out a little further if necessary.

Categories