I have a Spring Boot REST API. Due to a security policy I need to have CSRF protection enabled for endpoints accessed by the browser. However, this API will also be accessed by non-browsers. Is there a way I can create two sets of endpoints, one accessible by browsers only with CSRF enabled and the other accessible by non-browsers only with CSRF disabled?
-
Please provide enough code so others can better understand or reproduce the problem. – Community Mar 18 '22 at 15:32
3 Answers
When you configure your CSRF protection using the DSL, like this http.csrf()...
you can tell which requests you want the CSRF protection to be applied by passing a RequestMatcher
, like so:
http.csrf(csrf -> csrf.requireCsrfProtectionMatcher(new MyBrowserRequestMatcher()));
And your implementation of RequestMatcher
could verify if the HttpServletRequest
contains the header X-Requested-With: XMLHttpRequest
or check the User-Agent
.
Just keep in mind that the headers can be changed and you have no guarantee that the request actually come from a browser or non-browser app.

- 4,635
- 9
- 27
I think you could have separate URL bases for the browser requests and API requests.
For example, you could have all the endpoints that are to be queried by non-browsers under /api/...
and in your SpringBootSecurityConfiguration
class and configure(HttpSecurity http)
method you could conditionally disable CSRF with http.csrf().disable();
if the pattern matches (great tutorial can be found here)
Edit: here is another answer that might be useful.

- 466
- 2
- 7
-
1Thanks for the input. However, wouldn't that non-browser endpoint set still be accessible by browsers? Is there a way I can restrict a given endpoint to one client type or another? – R. Overbeck Mar 18 '22 at 16:18
-
You could set up roles and authorities, and make the API users have different level of security to users that signed up through the browser. Maybe that's a possible approach for you? In the end, a browser is just making the requests on behalf of the user, so in theory there really isn't a difference by type as far as I know, but you could enforce that separation manually. Is your main concern security and ensuring that the browser (end users) don't stumble onto an unsage endpoint? – ferrouskid Mar 18 '22 at 21:00
As @ferrouskid said, I created two URL one for browsers and other for non-browsers:
In spring security config:
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().ignoringAntMatchers("/withoutCsrf/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.cors().disable();
//complete your configuration }
In controller:
@Controller
@RequestMapping({"books","withoutCsrf/books"})
public class BookController {}

- 407
- 5
- 10