Receive Authorization header on anonymous url using Spring Boot - spring

How can an Authorization header be accessed on anonymous urls?
My security configuration looks like:
http
.authorizeRequests()
.antMatchers("/login", "/legacy-login").anonymous()
.antMatchers("/things/*").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
Authentication in general works fine. However, on /legacy-login I need to do some migration and need to access the authorization header without spring boot managing the authorization. Although /legacy-login is marked anonymous as soon as there are requests, spring intercepts the request and tries to authorize itself (what then results into 401).
How can I make Spring let the auth header through on that single url?

I foudn one solution myself. Instead of fiddleing around with .anonymous() and .permitAll() I added /legacy-login as ignore rule:
override fun configure(web: WebSecurity) {
super.configure(web)
web.ignoring()
.antMatchers("/legacy-login")
}

Related

How does one use different session creation policies for UI and REST endpoints with Spring Security?

I have an application that contains both a UI and some REST endpoints. The UI uses SAML login (the old Spring Security SAML extension) and the REST endpoints using a custom authentication. The REST endpoints are only called by external applications.
For the REST endpoints ("/api/**") I have stated a stateless session creation policy and for the rest of the endpoint no session creation policy at all (I also tried with ALWAYS as in the below example).
Prior to some Spring Boot version, not sure which, this worked. Currently I'm using Spring Boot v.2.6.1. The UI endpoint got the authentication object from the Http session.
But now it doesn't work. The security context object cannot be found in the Http session using the default HttpSessionSecurityContextRepository implementation. It is saved but it can't be restored.
So is it possible to use two session creation policy, one for the REST and the other for the UI part, or should this be handled in a different way?
Now it seems that the stateless session creation policy is also used by the UI, which is not intended.
I'm using two WebSecurityConfigurerAdapter classes; one for the API and the other for the UI.
After a successful SAML login the redirect URL now contains the ";jsessionid=6051854D94A0771BB9B99FE573AA4DFD" parameter. Probably because of the stateless policy...?
protected void configure(HttpSecurity http) throws Exception {
List<AbstractAuthenticationProcessingFilter> authFilters = new ArrayList<>();
authFilters.add(new OAuthMacAuthenticationProcessingFilter(authenticationManager(), this.properties));
ApiAuthenticationProcessingFilter apiAuthenticationProcessingFilter = new ApiAuthenticationProcessingFilter(authenticationManager(),authFilters);
http
.csrf()
.disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterBefore(apiAuthenticationProcessingFilter, BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
and for the UI part
protected void configure(HttpSecurity http) throws Exception {
http.securityContext().securityContextRepository(customSessionSecurityContextRepository);
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);
var auth = http
.authorizeRequests()
.antMatchers("/saml/**").permitAll()
.antMatchers("/loggedout/**").permitAll()
.antMatchers("/error").permitAll();
auth
.anyRequest()
.authenticated();
http.csrf().disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.headers().frameOptions().sameOrigin();
http.exceptionHandling().accessDeniedHandler(this.accessDeniedHandler());
http
.logout()
.disable(); // The logout procedure is already handled by SAML filters.
}
I'll answer this myself. The above code does actually work. The problem was on the remote end, the IDP I was using had some problems that day that resulted in that it didn't work as expected. The day after, it worked.

Spring OAuth2 security concerns

I am currently implementing Authorization_Code type OAuth2 flow to have single-sign-on (SSO) on my website.
Here is my code that enables it.
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests()
.antMatchers("/login", "/authorize", "/error")
.permitAll()
.anyRequest()
.authenticated()
.and().formLogin().loginPage("https://sso.mywebsite.com").loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/success",true)
.failureUrl("/error").permitAll()
.and()
.csrf()
.disable();
// #formatter:on
}
My concern is described below.
To make a login request (with username and password), sso.mywebsite.com should make a POST request to my oAuth service - http://oauth/perform_login?username=USERNAME&password=PASSWORD.
I tried it with Postman and it works. However, isn't this a security problem to send plain username and password like above in query param? I thought exposing user credential in uri (query param) could get captured by various network sniffing tools.
Is there a way to do this in different method?
As long as you are using HTTPS, your query parameters will be secure.
I am unclear, why your SSO website should make a POST to that URL (and also, why instead of having a POST body, append the parameters via the url). Shouldn't it rather "redirect" to the login page/authorization server or is the code above from your authorization server? It was a bit unclear from your description.

How to allow certain endpoint in spring security to be allowed without authentication?

I have Spring Boot Rest API web app in which I am using spring security to have most endpoints to require authentication.
This is part of code:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
....
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers("/oauth/token")
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
}
Can some one explain what each line begining with http.csrf means?
How can I modify above code so that enpoint /bars/pk can be allowed to be accessed without requiring authentication just as if there was no Spring Security ?
By default, Spring Boot activates protection against CSRF attack (Cross Site Request Forgery attack). The attack consists of a malicious site taking advantage of a user being alredy authenticated to a site (e.g. bank) in order to trick the user to do actions on that site (e.g. fund transfer).
The protection against the attack consists of the Spring Boot application sending a token with every response and expecting the token to be sent by the client on subsequent request. If the token is not received, Spring Boot returns an error.
Sometimes, you want to disable this behavior (at your own risks), so you use csrf.disable. You might find it convenient to disable csrf protection if you develop a Stateless API, and you have no way to link a POST request to any previous requests or session. But again, you need to consider this and reason about it carefully.
Please note that CSRF protection has not effect on GET requests. It only affects state chaning requests (e.g. POST, DELETE)
In order to allow your endoints to anyone, without requiring any authentication, you need to use
http.authorizeRequests().antMatchers("/**").permitAll();
EDIT
To specifically allow unauthorized requests to /bars/pk and keep the other elements unchanged, modify your code as follows :
http.csrf().disable()
.requestMatchers()
.antMatchers("/oauth/token")
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/bars/pk").permitAll()
.anyRequest().authenticated();
Here is a complete example:
httpSecurity.authorizeRequests()
.antMatchers(HttpMethod.GET)
.permitAll() // Allow all GET requests to go unauthenticated
.antMatchers(allowedResources)
.permitAll() // Allow all requests to go unauthenticated for the specified paths
.antMatchers(protectedResources).hasRole(USER)
.antMatchers(adminResources).hasRole(ADMIN)
.anyRequest().authenticated(); // Authenticate all other request paths

Spring Security returns 401 for unsecured URL

Using Spring Security 4.0.3 from a Spring Boot 1.3.3 application.
The application has two types of HTTP contents : "API" a REST API and "UI" a web based used interface (Thymeleaf + Spring Web MVC).
Most endpoints of the REST API of the application are secured, using Basic, but some are not and should always be available.
The simplified configuration looks like:
// In one method, the security config for the "API"
httpSecurity
.antMatcher("/api/**")
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.authorizeRequests()
.antMatchers("/api/ping").permitAll()
.antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic();
// In another method, the security config for the "UI"
httpSecurity
.authorizeRequests()
.antMatchers("/ui/", "/ui/index.html", "/ui/css/*", "/ui/js/*", "/ui/img/*").permitAll()
.antMatchers("/ui/user/**").hasRole("USER")
.antMatchers("/ui/**").denyAll()
.and()
.formLogin().loginPage("/ui/login.html").permitAll().failureUrl("/ui/login.html").defaultSuccessUrl("/ui/user/main.html")
.and()
.logout().logoutUrl("/ui/logout").permitAll().logoutSuccessUrl("/ui/login.html")
.and()
.httpBasic();
Accesses to secured endpoints work as expected.
But accesses to public endpoints such as ".../api/ping" fail with a 401 when the user provided an invalid Basic authentication. Of course such endpoints works fine when no or valid Basic authentication is provided.
This 401 from Spring Security is surprising. How can we implement a Spring Security configuration that never returns any 401 or 403 for selected endpoints?
Thank you for your time.
Update 1 : added info about the "UI" existence and security config
Order is important. Most specific rule (path) first:
httpSecurity
.antMatchers("/api/ping").permitAll()
// and then the rest
This because if there is a match like on antMatcher("/api/**"), Spring Security will not evaluate later rules.

How to use use `with(user(` when using Spring Session/Security in REST environment

Is it possible to pouplate a Test User with SecurityMockMvcRequestPostProcessors.user when using Spring Session with HeaderHttpSessionStrategy.
I tried something like:
mockMvc.perform(
get(URL)
.with(user("user").password("pwd").roles("USER", "ADMIN")))
.andExpect(status().isOk())
But it returns a 403.
Without the with(user( I get the a 401 so there is a difference.
I've a faily simple SecurityConfig containing:
http
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/api/**").hasAuthority("USER")
.and()
.csrf()
.disable()
.httpBasic()
.and()
.requestCache()
.requestCache(new NullRequestCache());
I's very similar like https://github.com/spring-projects/spring-session/tree/1.0.1.RELEASE/samples/rest as I have an endpoint with I authenticate to with http basic. It returns the authentication token via the header which is then used in other REST calls.
So I was hoping that I just could use the with(user( in this scenario to make my tests easier.
Yes you should be able to do this. The problem is that
.with(user("user").password("pwd").roles("USER", "ADMIN")))
is applying roles which means the ROLE_ prefix is automatically added.
In contrast, your configuration is using:
.antMatchers("/api/**").hasAuthority("USER")
which does not automatically add the ROLE_ prefix. Instead, try using:
.antMatchers("/api/**").hasRole("USER")
NOTE: I have also updated the tests to include an example of with(user("user")).

Resources