最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Spring validating constraints before security scopes - Stack Overflow

programmeradmin4浏览0评论

I have a Spring Boot 3.4 service secured with Spring Security OAuth2.

My security configuration is standard:

    @Bean
    public SecurityFilterChain oauth2FilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(sm -> sm.sessionCreationPolicy(STATELESS))
                .exceptionHandling(eh -> eh.authenticationEntryPoint(handleException()));

        http.securityMatcher("/api/**")
                .authorizeHttpRequests(auth ->
                        auth.requestMatchers("/api/**").authenticated())
                .oauth2ResourceServer(resourceServer ->
                        resourceServer.jwt(jwt -> jwt.decoder(jwtDecoder())));

        return http.build();
    }

My endpoints bear the following annotation: @PreAuthorize(hasAuthority('SCOPE_MY_SCOPE')).

Furthermore, on my API models, I do validation using Jakarta Validation 3 (e.g., using annotations like @NotNull on fields).

Given this configuration, I have the following scenario: I make an invalid request using a security bearer token that does not have the required scope.

The server responds with a 400, as the validation constraints fail. However, I expected to see a 403 being given this request; I think that Spring is exposing unnecessary information about my validation constraints to someone that is not authorized to call my endpoint. If I use a valid request, it indeed returns a 403, so the problem is with the order of the checks: I would like to see the security check, then the validation check, but seems it is going the other way around.

I have a Spring Boot 3.4 service secured with Spring Security OAuth2.

My security configuration is standard:

    @Bean
    public SecurityFilterChain oauth2FilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(sm -> sm.sessionCreationPolicy(STATELESS))
                .exceptionHandling(eh -> eh.authenticationEntryPoint(handleException()));

        http.securityMatcher("/api/**")
                .authorizeHttpRequests(auth ->
                        auth.requestMatchers("/api/**").authenticated())
                .oauth2ResourceServer(resourceServer ->
                        resourceServer.jwt(jwt -> jwt.decoder(jwtDecoder())));

        return http.build();
    }

My endpoints bear the following annotation: @PreAuthorize(hasAuthority('SCOPE_MY_SCOPE')).

Furthermore, on my API models, I do validation using Jakarta Validation 3 (e.g., using annotations like @NotNull on fields).

Given this configuration, I have the following scenario: I make an invalid request using a security bearer token that does not have the required scope.

The server responds with a 400, as the validation constraints fail. However, I expected to see a 403 being given this request; I think that Spring is exposing unnecessary information about my validation constraints to someone that is not authorized to call my endpoint. If I use a valid request, it indeed returns a 403, so the problem is with the order of the checks: I would like to see the security check, then the validation check, but seems it is going the other way around.

Share Improve this question asked 11 hours ago Daniel PopDaniel Pop 5511 gold badge7 silver badges33 bronze badges 3
  • 1 There isn't much you can do about this. First Spring Security does the request check (part of the SecurityFilterChain). Next the control is handed over to the RequestMappingHandlerAdapter which inspects the method, see the @Valid annotation and first does the validation. If that goes well it calls the method at which point the MethodSecurityInterceptor is being invoked to handle the security. As the MethodSecurityInterceptor relies on the method being executed (it is a before aspect) it will not be invoked before that. The only solution would be to do manual validation. – M. Deinum Commented 10 hours ago
  • That makes sense. Can you please add your comment as a response so I can approve it? – Daniel Pop Commented 10 hours ago
  • That's not correct. I have a Spring Boot 3.4.2 service and it works exactly how OP expects. If I send a valid request with a valid scope, it returns 200. If I send a valid request with an invalid scope it returns 401 and if I send an invalid request (with a query param that violates @Min) with an invalid scope it still returns 401. I'm using Hibernate validator and @ControllerAdvice to format errors nicer. I use @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) and @PreAuthorize("hasAnyAuthority('SCOPE_read', 'SCOPE_read-write')"). – SledgeHammer Commented 7 hours ago
Add a comment  | 

2 Answers 2

Reset to default 1

There isn't much you can do about this. First Spring Security does the request check (part of the SecurityFilterChain). Next the control is handed over to the RequestMappingHandlerAdapter which inspects the method, see the @Valid annotation and first does the validation. If that goes well it calls the method at which point the MethodSecurityInterceptor is being invoked to handle the security. As the MethodSecurityInterceptor relies on the method being executed (it is a before aspect) it will not be invoked before that.

The only solution would be to do manual validation in your controller. You can do this by injecting the validator into the controller and call it yourself. Drawback is that you will loose error translation etc. as well.

Can you give any example for the invalid JWT? If you receive 400 code, your JWT Token might be malformed. For example;

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1617201283,
  "scope": "read:data"
}

This token doesn't have "write:data" right. But if you send

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1617201283,
}

This token might be considered as malformed.

发布评论

评论列表(0)

  1. 暂无评论