Spring Boot version: 2.5.4

Consider the following controller:

@RestController
@Validated
public class FooController {

    @PutMapping(
        value    = "/lorem/{loremId}",
        consumes = "text/csv",
        produces = "application/json"
    )
    @PreAuthorize("@authz.isAuthorizedForXyz(#loremId, #ipsumId)")
    public ResponseEntity<List<Bar>> handlePutBar(@PathVariable @Size(max = 16) String loremId,
                                                  @RequestParam(name = "ipsumId") @Size(max = 32) String ipsumId,
                                                  @RequestBody @Valid BarsHolder barsHolder) {
        // ...
    }
}

In this case only barsHolder is validated before the method isAuthorizedForXyz() is called for authorization. Both loremId and ipsumId are validated afterwards, straight before entering the controller logic. Basically, the method used for authorization is receiving unvalidated input here.

(Adding @Valid prior to loremId / ipsumId won't change behavior...)

Enhancement:

Validate all input parameters before authorization logic is applied as already done for @RequestBody annotated parameters. This allows to have validation logic consistently on the controller method.

Related:

https://github.com/spring-projects/spring-security/issues/6545 https://github.com/spring-projects/spring-boot/issues/10157

Comment From: wilkinsona

Thanks for the suggestion. This is out of Spring Boot's control as it's Spring Framework that performs the validation of the method arguments. FWIW, I'm surprised by the difference in validation between the @RequestBody-annotated parameter and the @RequestParam-annotated parameters. We'll transfer this to the Framework team so that they can take a look.

Comment From: flix-

Any progress here? :)

Comment From: StevenPG

Also curious about any updates, my team is running into the same issue.

It poses a major problem for UUID parameters since the default UUID property editor adds padding for the PreAuthorize method parameter and then afterward runs the validation. So some invalid incoming UUIDs are made "valid" first using padding, authorized incorrectly (in our flow), and then validated uselessly.

Comment From: chuanhui2020

Also looking for some helpful updates cause our team are facing the same issue =><=

Comment From: valh1996

+1

In the meantime, do you know a workaround for using @Valid with @PreAuthorize? But have the role validation (@PreAutorize) done before please?

Comment From: Tockra

I run into the same issue. Did you find a work around for it?

Comment From: Aliaksie

+1 Any progress here? :)

Comment From: valh1996

Hi @Tockra & @Aliaksie,

My temporary solution is to protect these POST/PUT routes in a custom WebSecurityConfigurerAdapter using mvcMatchers :

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true )
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // code


        // Entry points
        http.authorizeRequests()
                .mvcMatchers(HttpMethod.PUT, "/v1/users/{userId}")
                    .access("hasAnyRole('ADMIN', 'HR') or (hasAnyRole('CANDIDATE', 'EMPLOYEE') and @authorizationComponent.canEditUser(principal, #userId))")

                .anyRequest().authenticated();

       // Apply JWT
        http.apply(new JwtTokenFilterConfigurer(jwtTokenProvider));
    }
}

Comment From: Tockra

Okay, thank you valh. I know, that I can do the authentification there, but I would like to continue to use the @PreAuthorize feature. I built following (not clear) solution. I wrote a class "ValidatorService" which seems like this:


@Service
@Validated
public class ValidateService {
    public MyDtoToValidate springValidation(@Valid final MyDtoToValidate myDtoToValidate) {
        return myDtoToValidate;
    }
}

Controller:

// Autowire this.validateService
   @GetMapping(value = "")
   @PreAuthorize("hasAuthority('SOME_RIGHT')")
    public void exampleEndpoint(@RequestBody final MyDtoToValid dto) {
         this.validationService.springValidation(dto);

         // do stuff
    }

I write a own springValidation method for each dto. That works in java.

€dit: With a little bit generic magic you don't need to add own methods to the validatorService:

@Service
@Validated
public class ValidateService {
    public <T> T springValidation(@Valid final T body) {
        return body;
    }
}

Comment From: valh1996

Okay, thank you valh. I know, that I can do the authentification there, but I would like to continue to use the @PreAuthorize feature. I built following (not clear) solution. I wrote a class "ValidatorService" which seems like this:

java @Service @Validated public class ValidateService { public MyDtoToValidate springValidation(@Valid final MyDtoToValidate myDtoToValidate) { return myDtoToValidate; } }

Controller:

```java // Autowire this.validateService @GetMapping(value = "") @PreAuthorize("hasAuthority('SOME_RIGHT')") public void exampleEndpoint(@RequestBody final MyDtoToValid dto) { this.validationService.springValidation(dto);

     // do stuff
}

```

I write a own springValidation method for each dto. That works in java.

This solution adds code and complicates things unlike the one I propose. The disadvantage is that there are role checks in two different places (in the controllers and in this file), but I think it's still better than adding an extra layer that adds maintenance.

Comment From: Tockra

two different places (in the controllers and in this file), but I think it's still better than adding an extra layer that adds maintenance.

I don't know. There is no further complex functionality. There is just one additional class and one simple method for each dto. It seems to be manageable. You need to add a seperate line in the controller method and a method, which has no functionality, but causes the @Valid check after the authorization check in the controller method. This is a small bunch of additional work, but fits better in our roles and rights concept than moving the check to the security class.

Comment From: rstoyanchev

Team Discussion: we don't necessarily see the order in which these are applied as incorrect. Security is applied first as an external concern and operates on the actual inputs. Validation, through parameter annotations, is more part of the method implementation and closer to its invocation (just a convenience for similar validation checks inside the controller method).

That said, BeanPostProcessors are ordered, and if you wanted to, you could try and set the order on the MethodValidationPostProcessor. Another alternative is to place security annotations could also be places on the service layer and have that invoked from the controller, with already validated inputs.

Keep in mind also that the current behavior, with the use of a bean post-processor and an AOP proxy for validation, is a result of an implementation detail since it's the only way to validate @RequestParam and @PathVariable arguments currently. In #24913 intend to make bean validation a first-class feature of Spring MVC and WebFlux, at which point will become more integrated with the controller method and come strictly after any AOP proxies like the security check.

Comment From: rstoyanchev

As for the difference with validation on @RequestBody, there is a known side effect of using MethodValidationPostProcessor where @Valid causes double validation, once as part of the AOP proxy applying method validation, and a second as part of the built-in support for validation for @RequestBody. This is part of what #24913 aims to resolve by making method validation a built-in feature of Spring MVC.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.