10

When I try to run an application it fails to start and throws this exception.

This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).

I am new to spring security please help me solve this error

this is my spring security configuration class

package com.ronit.SpringSecurityTutorial.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
@Configuration
public class SecurityConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    AuthenticationManager authManager(UserDetailsService detailsService) {
        DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
        daoProvider.setUserDetailsService(detailsService);
        return new ProviderManager(daoProvider);
    }

    @SuppressWarnings("removal")
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.csrf(csrf -> csrf.disable()).authorizeHttpRequests(auth -> {
            auth.anyRequest().authenticated();
            auth.requestMatchers("/auth/**").permitAll();
            auth.anyRequest().authenticated();
        }).httpBasic().and().build();
    }
}

This is the spring boot application

package com.ronit.SpringSecurityTutorial;

import java.util.HashSet;
import java.util.Set;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.ronit.SpringSecurityTutorial.models.ApplicationUser;
import com.ronit.SpringSecurityTutorial.models.Role;
import com.ronit.SpringSecurityTutorial.repository.RoleRepository;
import com.ronit.SpringSecurityTutorial.repository.UserRepository;

@SpringBootApplication
public class SpringSecurityTutorialApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityTutorialApplication.class,args);
    }

    @Bean
    CommandLineRunner run(RoleRepository roleRepository, UserRepository userRepository,
            PasswordEncoder passwordEncoder) {
        return args -> {
            if (roleRepository.findByAuthority("ADMIN").isPresent())
                return;
            Role adminRole = roleRepository.save(new Role("ADMIN"));
            roleRepository.save(new Role("USER"));

            Set<Role> roles = new HashSet<>();
            roles.add(adminRole);
            ApplicationUser admin = new 
            ApplicationUser(1, "Admin", passwordEncoder.encode("Password"), roles);
            userRepository.save(admin);
        };
    }
}

This are the dependencies in pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

This is the section of console where the error is occuring

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>

There is no configuration related to security in application.properties file.

I searched this on google and found some stack overflow pages but none of them were using similar configuration to mine.

I have watched an online tutorial and made this I followed each step correctly but my configuration is not working.

The application is stopping and getting terminated straight straight away.

I am using spring boot 3 and spring security 6 in my application's configuration

Arshad Ali
  • 3,082
  • 12
  • 56
  • 99
Ronit Pandey
  • 153
  • 1
  • 8
  • I am using a youtube tutorial to learn spring security. If anyone could tell me about other resources to stay updated with the latest version of spring security it would be appreciable thanks for reading my question – Ronit Pandey Aug 01 '23 at 08:17
  • I am unable to understand what are mvc and non-mvc patterns if anyone could explain it to me it would be a great deal – Ronit Pandey Aug 01 '23 at 08:18
  • Thank you for editing James it would be better if you could provide some help – Ronit Pandey Aug 01 '23 at 09:10
  • 1
    this solves your problem https://spring.io/security/cve-2023-34035 – Toerktumlare Aug 01 '23 at 11:30
  • @Toerktumlare is correct but the answer can be elaborated more – Raster R Aug 03 '23 at 05:28
  • One thing that is wrong with your configuration is that you have `auth.anyRequest().authenticated();` twice. Basically the first one already renders everything after that useless. This line has to come last not as the first, as the rules are consulted in the order they are defined in . – M. Deinum Aug 03 '23 at 09:00

3 Answers3

3

Am sure in future there will be better ways of dealing with this. But for now this seems to work. There are two options recommended by https://spring.io/security/cve-2023-34035:-

First Option:
Did a small very simple poc war web application in tomcat. Debugged this method -

org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.requestMatchers(HttpMethod method, String... patterns)

Studied https://spring.io/security/cve-2023-34035

Just before the below lines in above mentioned method I examined what was in the registrations object.

Assert.isTrue(registrations.size() == 1, "This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).");

Found it to be registrations of

org.apache.catalina.servlets.DefaultServlet against "default"
org.apache.jasper.servlet.JspServlet against "jsp"
org.springframework.web.servlet.DispatcherServlet against "dispatcher"

In https://spring.io/security/cve-2023-34035 they mention "Sometimes these extra servlets are not needed. For example, some servlet containers will add a DefaultServlet that DispatcherServlet effectively replaces. In many cases, such a servlet can be removed from your container's global configuration".

What they are saying is that we should edit apache-tomcat-10.1.10\conf\web.xml.

We should comment out the registrations in that web.xml for org.apache.catalina.servlets.DefaultServlet, org.apache.jasper.servlet.JspServlet. We should also remove the mapping entries in same web.xml

For other servlet containers we can consider similar concepts.

Second Option:
The other option is to do what they also suggested in "For an example mitigation, please see the following Spring Security sample application and the corresponding diff." https://spring.io/security/cve-2023-34035 namely follow https://github.com/spring-projects/spring-security-samples/commit/4e3bec904a5467db28ea33e25ac9d90524b53d66

In other words the example they give is Replace :

    .requestMatchers("/login", "/resources/**").permitAll()   

with

    .requestMatchers(mvc.pattern("/login"),
         mvc.pattern("/resources/**")).permitAll()  

In a web application you would usually use mvc matcher as shown here. You can also use ant matcher - there is a little risk in using ant matchers as explained here- Difference between antMatcher and mvcMatcher . In complex scenarios can use regexmatchers also.

Decision Point:

If you want to retain original code try editing the server's (not project's) web.xml-or similar configuration if not tomcat - but some other servlet container , to eliminate the extra servlets that come by default in servlet containers.
If you are willing to change code try the second option.

Also Note:

Sometimes if its legacy code and you are using DispatcherServlet methods in your code you will have to eliminate that to use first option or else settle for second option.

Hope this helps

Raghu
  • 31
  • 3
  • But how do I use it with Vaadin? I only have WebSecurity – Christoph S Aug 09 '23 at 07:04
  • @ChristophS haven't used Vaadin in a long while. Are you saying you faced the same issue in Vaadin recently? This is a recent spring boot latest issue. Where you using spring with Vaadin? – Raghu Aug 09 '23 at 08:09
  • Yes, I am using Vaadin with Spring Boot, upgraded to spring boot 3.1.2 and got this issue with the overriden webSecurity, but cannot change to mvc matcher https://vaadin.com/docs/latest/security/enabling-security with web.ignoring().requestMatchers("/actuator/health/**"); – Christoph S Aug 09 '23 at 14:08
  • @ChristophS . Sorry for delay. Can u share some sample code on a git repository. I do believe the answer lies in the steps I shared. That said if you share your code as a sample I could have a look and try to give you more specific suggestions. – Raghu Aug 15 '23 at 15:18
  • I looked into the Vaadin sources and they used an AntPathRequestMatcher for the ignoring parts. So I used this as well and it works. – Christoph S Aug 18 '23 at 07:48
  • @ChristophS That's the second option I indicated wherein you have to change existing code. You could also use mvcMatcher. First option is useful if you want to try achieving same without changing code but rather change the server configurations. Use either based on your convenience. I just provided both solutions so that its a complete picture. – Raghu Aug 18 '23 at 08:30
  • 1
    @ChristophS One more thing. Do test the security expressions and the results thoroughly because you chose the second option. I believe this was resolving to mvcmatcher before this issue. In the past there have been known subtle differences in how ant mactchers and mvcmatchers work. If you chose the code change route and went for AntMatcher do test the actual security especially about whether the actual url has or has not a trailing /. – Raghu Aug 18 '23 at 12:14
  • @CristophS Please refer - https://stackoverflow.com/questions/50536292/difference-between-antmatcher-and-mvcmatcher . Not sure if this is still valid or not. But do test ur fix more thoroughly because you chsoe the code change option and went for AntMatcher instead of mvcMatcher. Hope you find this tip helpful – Raghu Aug 18 '23 at 12:19
  • Yes, did a test. Works all. This was the only place where I needed this change, it configures the ignoring for my health check endpoint. – Christoph S Aug 18 '23 at 14:45
  • @ChristophS Noted. Anyone else reading this- prefer mvc matcher or the 1st option. You would not have had that deviaton for some urls as faced by ChristphS for his health check endpoint. Generally for web projects prefer mvc matcher. Ant matcher is basically needed when u dont have mvc or web concepts in your spring application. No harm in using it. Just use it with care if you use it. – Raghu Aug 18 '23 at 16:35
  • as explained here- https://stackoverflow.com/questions/50536292/difference-between-antmatcher-and-mvcmatcher and also in the previous edition of Spring Security in Action book be very careful when using AntMatchers – Raghu Aug 18 '23 at 16:37
3

A migration occurred due to vulnerability CVE-2023-34035.

In the event that you get an error like the following:

This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).

You should use a complete RequestMatcher.

For example, if an application has a servlet deployed to /my-servlet/* and is authorizing that traffic like so:

@Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers("/my-servlet/**").hasRole("USER")
            .requestMatchers("/spring-mvc-controller/**").hasRole("USER")
            .anyRequest().authenticated()
        )
        // ...
    return http.build();
}

then, the application should instead do the following:

import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

@Bean
MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
    return new MvcRequestMatcher.Builder(introspector);
}

@Bean
SecurityFilterChain appSecurity(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers(antMatcher("/my-servlet/*")).hasRole("USER")
            .requestMatchers(mvc.pattern("/spring-mvc-controller/**")).hasRole("USER")
            .anyRequest().authenticated()
        )
        // ...
    return http.build();
}

For more details read this repository offered by Spring: cve-2023-34035-mitigations

1

There is small configuration which will prevent us from:

This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).

error. It is clearly mentoined at Configuration Migrations

If you are having problem with the new requestMatchers methods, you can always switch back to the RequestMatcher implementation that you were using. For example, if you still want to use AntPathRequestMatcher and RegexRequestMatcher implementations, you can use the requestMatchers method that accepts a RequestMatcher instance:

Okay! In the context of your issue mentoined in the code above you should try to add antMatcher(pattern) as parameter to requestMatchers() as shown below:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http.csrf(csrf -> csrf.disable())
           .authorizeHttpRequests(auth -> {
            auth.anyRequest().authenticated();
            auth.requestMatchers(antMatcher("/auth/**")).permitAll();
            auth.anyRequest().authenticated();
        }).httpBasic().and().build();
    }

Hope it will solve your issue.

Arshad Ali
  • 3,082
  • 12
  • 56
  • 99