尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求

Posted

技术标签:

【中文标题】尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求【英文标题】:Spring Security blocks POST requests despite SecurityConfig 【发布时间】:2018-12-04 05:15:47 【问题描述】:

我正在开发一个基于 Spring Boot (spring-boot-starter-web) 的 REST API,我使用 Spring Security (spring-security-core e spring-security-config) 来保护不同的端点。

身份验证是通过使用本地数据库完成的,该数据库包含具有两组不同角色的用户:ADMINUSERUSER 应该能够GET 所有API 端点和POST 到基于routeA 的端点。 ADMIN 应该能够与USER 加上POSTDELETE 对基于`routeB

的端点执行相同的操作

但是我得到的行为是我可以向任何端点发出 GET 请求,但 POST 请求总是为任何类型的用户返回 HTTP 403 Forbidden - ADMINUSER - 这不是预期的根据我的SecurityConfiguration,我期待什么。

关于我缺少什么的任何想法?


SecurityConfiguration.java

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Autowired
    private RESTAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private DataSource dataSource;

    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception 
        logger.info("Using database as the authentication provider.");
        builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(new BCryptPasswordEncoder());
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
            authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
                               .antMatchers(HttpMethod.POST, "/routeA/*").hasAnyRole("ADMIN", "USER")
                               .antMatchers(HttpMethod.POST, "/routeB/*").hasRole("ADMIN")
                               .antMatchers(HttpMethod.DELETE, "/routeB/*").hasRole("ADMIN").and().
            requestCache().requestCache(new NullRequestCache()).and().
            httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
            cors();
    

    @Bean
    public CorsConfigurationSource corsConfigurationSource() 
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

RouteBController .java

@RestController
public class RouteBController 

    static final Logger logger = LoggerFactory.getLogger(RouteBController.class);

    public RouteBController()  

    @RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET)
    public String getStuff() 
        return "Got a hello world!";
    

    @RequestMapping(value = "routeB", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
    public String postStuff() 
        return "Posted a hello world!";
    


RESTAuthenticationEntryPoint.java

@Component
public class RESTAuthenticationEntryPoint extends BasicAuthenticationEntryPoint 

    @Override
    public void afterPropertiesSet() throws Exception 
        setRealmName("AppNameHere");
        super.afterPropertiesSet();
    

【问题讨论】:

【参考方案1】:

禁用 CSFR 以解决此问题之前,请查看 Mohd Waseem's answer 上的资源,以更好地了解它的重要性并了解它的重要性正确设置。作为RCaetano has said,CSFR 可以帮助我们免受攻击,不应盲目禁用它

由于这个答案仍然解释了我原来的问题的 2 个问题,我将把它作为标记的答案,以提高人们对 CSFT 和安全路线可能存在的问题的认识,但 不要从字面上理解.


SecurityConfiguration.java 中有 2 个问题导致其行为不端。

虽然403 Forbidden 错误消息没有包含任何关于失败原因的消息指示(参见下面的示例),但事实证明这是由于启用了CSRF。禁用它允许处理 POSTDELETE 请求。


    "timestamp": "2018-06-26T09:17:19.672+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/routeB"

另外,antMatched(HttpMethod, String) 中用于RouteB 的表达式也不正确,因为/routeB/* 期望它在/ 之后有一些东西。正确的配置是/routeB/**,因为可以存在更多路径(或不存在)。


更正SecurityConfiguration.java

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().
        authorizeRequests().antMatchers(HttpMethod.GET, "/**").hasAnyRole("ADMIN", "USER")
                           .antMatchers(HttpMethod.POST, "/routeA/**").hasAnyRole("ADMIN", "USER")
                           .antMatchers(HttpMethod.POST, "/routeB/**").hasRole("ADMIN")
                           .antMatchers(HttpMethod.DELETE, "/routeB/**").hasRole("ADMIN").and().
        requestCache().requestCache(new NullRequestCache()).and().
        httpBasic().authenticationEntryPoint(authenticationEntryPoint).and().
        cors().and().
        csrf().disable();


来源: *** em Português

【讨论】:

CSRF...呸!谢谢! 我知道这个问题已经存在几个月/几年了,但据我所知,CSRF 不应该在生产中被禁用,不是吗? @Igor 正如在这些 cmets 中所说,不应盲目禁用 CSRF。您可以根据自己的需要对其进行调整,以避免我遇到的问题,同时保持它提供的安全优势 @RCaetano 感谢您的评论,我没有看到之前有人评论过。我会留下答案,因为我相信它会引起人们对 CSFT 和安全路由可能存在的问题的认识,但我指出了这篇文章中分享的更好的解决方案/资源。 大量帮助找到这个问题!除此之外,如果您想选择性地禁用 CSRF(例如在公开公开的 API 上),您可以将 ANT 匹配器添加到 csrf。例如.csrf().ignoringAntMatchers("/some/rest/api")【参考方案2】:

跨站请求伪造是一种网络安全漏洞,允许攻击者诱使用户执行他们没有执行的操作 打算执行。

在您的情况下,禁用 CSRF 保护会使用户面临此漏洞。

注意:如果它是带有 O-Auth 保护的纯 Rest API,那么 CSRF 不是 需要。 Should I use CSRF protection on Rest API endpoints?

但是在您的情况下,当用户登录会话并返回 cookie 作为响应并且没有 CSRF 令牌时,攻击者可以利用它并执行 CSRF

禁用 CSRF 并不是一个好主意,您可以将应用配置为在响应标头中返回 CSRF 令牌,然后在所有后续状态更改调用中使用它。

在您的 SecurityConfiguration.java

中添加这行代码
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);

CsrfTokenResponseHeaderBindingFilter.java

public class CsrfTokenResponseHeaderBindingFilter extends OncePerRequestFilter 
    protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
    protected static final String RESPONSE_HEADER_NAME = "X-CSRF-HEADER";
    protected static final String RESPONSE_PARAM_NAME = "X-CSRF-PARAM";
    protected static final String RESPONSE_TOKEN_NAME = "X-CSRF-TOKEN";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, javax.servlet.FilterChain filterChain) throws ServletException, IOException 
        CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);

        if (token != null) 
            response.setHeader(RESPONSE_HEADER_NAME, token.getHeaderName());
            response.setHeader(RESPONSE_PARAM_NAME, token.getParameterName());
            response.setHeader(RESPONSE_TOKEN_NAME, token.getToken());
        

        filterChain.doFilter(request, response);
    

标头响应表单服务器:

请注意,我们现在在标头中有 CSRF 令牌。在会话到期之前,这不会改变。 另请阅读:Spring Security’s CSRF protection for REST services: the client side and the server side 以便更好地理解。

【讨论】:

(Others) 一定要使用正确的导入:org.springframework.security.web.csrf.CsrfToken 或者你可以得到:class org.springframework.security.web.csrf.DefaultCsrfToken cannot be cast to class org.springframework.security.web.server.csrf.CsrfToken【参考方案3】:

这是一个简单的 CSRF 启用问题,不允许 POST 请求。我遇到了同样的问题,这是解决方案:(解释

@Override
protected void configure(HttpSecurity http) throws Exception 
    http
        .authorizeRequests()
            .antMatchers(HttpMethod.POST,"/form").hasRole("ADMIN")  // Specific api method request based on role.
            .antMatchers("/home","/basic").permitAll()  // permited urls to guest users(without login).
            .anyRequest().authenticated()
            .and()
        .formLogin()       // not specified form page to use default login page of spring security
            .permitAll()
             .and()
        .logout().deleteCookies("JSESSIONID")  // delete memory of browser after logout
         
        .and()
        .rememberMe().key("uniqueAndSecret"); // remember me check box enabled.
    
    http.csrf().disable();  // ADD THIS CODE TO DISABLE CSRF IN PROJECT.**

以上代码:

http.csrf().disable();

会解决问题。

【讨论】:

以上是关于尽管有 SecurityConfig,但 Spring Security 会阻止 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

是否可以拆分spring的SecurityConfig?

无法在 SpringBootApplication 中关闭 SecurityConfig

页面重定向取决于使用 Spring 安全性和 Thymeleaf 的角色 - Spring

UnsatisfiedDependencyException:创建名为“securityConfig”的 bean 时出错

SecurityConfig 中出现 SpringSecurity + AWS Cognito 连接错误

在 grails + acegi 中测试 securityConfig 映射