Spring Security, Stateless REST service and CSRF - java

I have a REST service, built using Java, Spring-boot and using Spring Security with Basic Access Authentication. There are no Views, no JSP etc, no 'login', just stateless services which can be called from a React app hosted separately.
I've read a variety of documentation about CSRF protection, but can't decide whether I should be using spring-security CSRF config, or just disabling it? If I disable the csrf protection I can call the service with curl using my basic auth like this:
curl -H "authorization:Basic c35sdfsdfjpzYzB0dDFzaHA=" -H "content-type:application/json" -d '{"username":"user","password":"password","roles":"USER"}' localhost:8081/api/v1/user
If I enable the csrf protection and provide a x-csrf-token header, then the spring CsrfFilter attempts to cross check this against a value from (I think) a session cookie in the HttpServletRequest. However since its a stateless REST service I don't have a session, and haven't 'logged in'.
I have a config class which looks like this:
#EnableWebSecurity
#Configuration
public class ServiceSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and().httpBasic();
if (!serviceProperties.isCsrfEnabled()) {
http.csrf().disable();
}
}
The more I think about it, the more it seems that I will just need to disable CSRF protection. Is there another way to configure spring security so it will work?
thanks

To answer your first question, in the context that you describe, you do not need CSRF protection. The background of CSRF protection is to ensure that the user is not tricked into doing some unwanted action.
For example, in pure theory, you could have logged into a bank's website (and thus established a session) and then went to some shady website. This site could have a form making a POST request to the bank's APIs. Because you have a session there, if the endpoint is not CSRF protected, then the request may go through.
As such, CSRF mostly acts as a protection against browser + session based attacks. If you expose a pure REST API with e.g. OAuth protection, then I don't see any reason for CSRF.
As you use spring boot, you could also disable CSRF using the application.properties / application.yaml configuration file.
security.enable-csrf=false
You can check out the Common Application Properties documentation page for more out-of-the-box configuration options.

If you want to disable csrf in more proper way you can call it like this(if using java configuration)
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and().httpBasic();
.and()
.csrf()
.disable()

Related

Spring 5 Authorization not working as expected

I am new to Spring Security. We are using Spring Security 5.4.5 with Spring Boot in one of my sample examples.
I have below config class in which am trying to apply the Spring Security authentication/authorization in /user and /admin endpoints of the REST API.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
PasswordEncoder bcryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().principal("guest").authorities("GUEST_ROLE")//Provide the name and role to the annonymous user
.and()
.authorizeRequests()
.antMatchers("/register").anonymous()//allows registration page to be accessed by annonymous users only
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/admin").hasAnyRole("ADMIN_ROLE")
.antMatchers(HttpMethod.GET,"/user").hasAnyRole("STUDENT_ROLE", "ADMIN_ROLE")
.and()
.httpBasic();
}
#Override
#Bean
protected UserDetailsService userDetailsService() {
UserDetails annaSmithUserDetails = User.builder()
.username("annasmith")
.password(bcryptPasswordEncoder.encode("password"))//default password enoder is bcrypt
.roles("STUDENT_ROLE", "ADMIN_ROLE") //role of the user
.authorities("STUDENT_READ","STUDENT_WRITE","COURSE_READ","COURSE_WRITE") //authorities or we can say permission assigned to the user
.build();
return new InMemoryUserDetailsManager(annaSmithUserDetails);//can configure different
}
}
As per the above Spring configuration /user will be accessible to both the USER and ADMIN role and /admin will be accessible to the ADMIN role.
When am trying to access /user in the browser it displays the username and password popup and once I enter the correct credentials of the configured user it is not working and gives the 403 error.
I have below three questions
Am not seeing any error in the console log and is there a way I can see why Spring Security is showing the 403 error?
What is the issue with the above Spring Security configuration as I am not able to access the REST API endpoints?
Am not seeing any error in the console log and is there a way I can see why spring security is showing the 403 error?
By enabling spring debug logs, how to do this can be done with a simple google search or found in the spring documentation. Learn to debug your application (debugging your application should always be the first thing you learn and should be done before asking on stack overflow).
What is the issue with the above spring security configuration as I am not able to access the REST API endpoints?
Could be several issues since you have not disclosed how you are accessing the application. By curl, web browser, another webclient using fetch in a react application etc. etc. Should also be included when you ask on stack overflow so that people can be able to reproduce the issue at hand.
But listing some of the things that can be wrong:
Your request is done improperly
Your password might not be correct, because i see you are encrypting your password incorrectly (see the documentation of how to actually do it)
ensure your password is stored with the correct prefix, or use UserBuilder users = User.withDefaultPasswordEncoder(); when building your user as in the docs.
Roles should be defined without prefixes or suffixes (_ROLE) if to follow any standard
after you are logged in are you redirected to something you are not allowed to access?
As you can see there are several things that can be wrong, but you have provided too little information to be able to answer, and there is a lot of things you can do before asking on stack overflow.
The answer is vague since the question is vague.

reactjs with proxy not getting anonymous session from spring boot backend

I'm trying to use create-react-app to start the frontend for my spring-boot project.
I use redis to store sessions.
For some reason I actually need to enable session generation for anonymous users.
The following code is my security config for spring boot:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//#formatter:off
http
.formLogin()
.loginPage("/login")
.permitAll()
.loginProcessingUrl("/form-login")
.and()
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/restricted/**")
.authenticated()
.antMatchers("/**")
.permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
//#formatter:on
}
}
I've tested this by having a very simple index.html inside the "static" folder under my spring-boot project. Then I go to "http://localhost:8080" and I saw the session cookie.
Then I removed "index.html" file and start the react app with proxy config. By default the new url is "http://localhost:3000". I do see the default reactjs startup page but I no longer get any session cookies.
My proxy setting in "package.json" for my create-react-app is as follows:
"proxy": "http://localhost:8080"
I also tested that I can still get session cookie only if I directly go to "http://localhost:8080" instead of port 3000.
Any help is appreciated. Thanks so much in advance.
Okay people, after waiting for a couple days without any answers I did some research myself and found the answer. I decided to post the answer here in case other people having the same issue.
The issue with create-react-app proxy is that it is a forward proxy. So the session cookie doesn't really work well with forward proxy. Sadly there is no easy solution solving this issue but there is a workaround.
As I mentioned above, I can for sure get the session cookie by directly accessing the backend url. So if I want to get the session cookie by using the proxy, I should put some code in the frontend to access the backend first and then put the session cookie in the header whenever the app starts. Then keep monitoring the frontend and reacquire the session whenever it expires.
For the best practice, the backend should really have a mock service which has no session, no login and no tokens but mocked data. Because the frontend doesn't really care about how session, access token or login works, these are the backend jobs. The frontend just need to get the data and then display the data.
But in reality having a mock server may take time and it is not worth doing that for every thing case.
So if you do not want to write the mock server, you either go with proxy but have a little hack in your frontend to actually acquire the session. Or you build the entire frontend app and put it under the "static" folder in your spring boot app.
For me I would rather separate the frontend and backend rather than putting them all together.

Spring Boot Security without Thymeleaf

I'd like to secure all pages in "/static/secure" in Spring Boot utilizing Spring Security, but I do not wish to utilize a view.
I have created a login form, with method="POST", and have setup Java-Based configuration class to go to /static/secure/main.html on success.
I wish I could approach from a specific problem standpoint, unfortunately everything I can find for guidance on how to authenticate uses Thymeleaf, or a view. I really just want it to navigate to /static/secure/main.html upon successful login, not call a view. Does anyone know how to configure Spring Boot to just go to a regular HTML page upon authentication?
I am attempting to just use Spring Security to handle login, then the rest of the app will be Angular once we're in, hitting REST API endpoints as needed - so I don't really want the "view" concept, nor Thymeleaf in the mix.
Any help would be much appreciated!
Thank you,
Dan
Successful authentication redirects to the page the user initially tried to open. So as long as you secured your /static/secure folder, accessing /static/secure/main.html will trigger authentication and then automatically redirect to that page on success.
You configuration overall will look something like this:
#Configuration
#EnableWebMvc
public class SpringMvcConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
}
(Given you serve your Angular pages from /static/... URLs and the pages themselves go from /src/webapp/static folder in your Maven project.)
In your Spring Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/static/secure/**").authenticated()
.and()
// ...
}

SpringBoot - Deactivate security completely to enable digest forwarding

We are building an app based on SpringBoot that shall work like a proxy. It accepts requests and sends them to another endpoint. This includes the digest authentication.
Client --> Proxy (Spring) --> Resource
The client and resource use digest authentication, which works fine without the proxy.
How can I completely disable any security, but especially springs default handling of digest authentication, to plainly forward the request (no need to discuss potential security issues, this is intended).
We tried .authorizeRequests().antMatchers("/**").permitAll(), security.ignored=/**, management.security.enabled=false and
#EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class})
I tested that digest forwarding is technically on a NodeJS/Express stack.
You have to ignore any request. Also, you have to remove, if exists, the servlet's filters over digest.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}

Spring Boot Security and Auth0 - Cannot disable CSRF

I am building a RESTful Spring Boot and React/Alt application. I want to add spring security to make sure that there could be no unauthenticated requests to the API.
I am using Auth0 for an authentication provider so users can log in to the application and more specifically the spring-security-auth0 library to handle the server side security side of things.
https://github.com/auth0/spring-security-auth0
After following the basic tutorial on Auth0, I have configured the security config in my spring application so that requests to the API will not work without a JSON web token. This works, but there are some endpoints (those that need a POST) in the controllers that don't. I get this error in Chrome devtools -
Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'
I understand that there are no CSRF headers present and that is why it isn't working. But the main reason I am confused is because I have disabled CSRF and it still happens. As the spring docs instruct, I've done this -
#Configuration
#EnableWebSecurity
#ComponentScan("com.auth0")
#ImportResource("classpath:auth0-security-context.xml")
#PropertySource("classpath:auth0.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
I don't need CSRF in this case as the app will only ever be used in a dev environment. Why does this still happen even though I have disabled CSRF completely?
The CsrfFilter that is responsible for the Exception is definetly enabled.
This can have various reasons. Can it be you have another instance of WebSecurityConfigurerAdapter configured somewhere ?
You can try the following :
Set a breakpoint in line 'http.csrf().disable();' to see if its really executed.
Set breakpoints in the constructor(s) of WebSecurityConfigurerAdapter to see whether many instances are created.
This should show you whats going wrong..

Categories