将 Spring Security(双向 SSL)配置为仅对 CORS 预检 OPTIONS 请求进行身份验证

Posted

技术标签:

【中文标题】将 Spring Security(双向 SSL)配置为仅对 CORS 预检 OPTIONS 请求进行身份验证【英文标题】:Configuring Spring Security (two-way SSL) to not require authentication only on CORS preflight OPTIONS requests 【发布时间】:2018-08-23 10:06:56 【问题描述】:

我一直在试图找出为什么跨域请求仅在 Firefox 中失败。事实证明 Firefox 不会在跨域 XHR 上发送 SSL/TLS 凭据,这显然是由 W3 CORS Spec 定义的。我能够大部分通过在我的客户端代码中向我的 XHR 实例添加 withCredentials: true 来解决这个问题(它适用于 GET、PUT、POST、DELETE 等)。

然而,尽管我在 XHR 中明确告知,Firefox 仍然拒绝向飞行前 OPTIONS 请求添加凭据。除非有某种方法可以在客户端代码中执行此操作,否则我将配置我的服务器以允许未经身份验证的 OPTIONS 请求(这在 2 路 SSL 配置中对我来说似乎很疯狂)。这里有安全风险吗?

我们的服务器是一个使用 Spring Security 的 Spring Boot 项目,并使用 X509 安全证书配置为 2-way SSL。我将提供所有相关代码,让您了解应用程序的配置方式。

主类:

@EnableAutoConfiguration
@SpringBootApplication
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OurApp extends WebSecurityConfigurerAdapter 

    @Autowired
    OurUserDetailsService ourUserDetailsService;

    public static void main(String[] args) throws Exception 
        ConfigurableApplicationContext ctx = SpringApplication.run(OurApp.class, args);
    

    @Override
    public void configure(WebSecurity web) throws Exception 
      web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        // X509Configurer<HttpSecurity> x509Configurer = http.authorizeRequests().anyRequest().authenticated().and().x509();

        http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().
            .anyRequest().authenticated()
            .and()
            .x509().subjectPrincipalRegex("(.*)").authenticationUserDetailsService(ourUserDetailsService)
            .and()
            .cors()
            .and()
            .csrf().disable();
    

具有 CORS 设置的配置类:

@Configuration
public class OurConfig 
    @Bean
    public CorsConfigurationSource corsConfigurationSource() 
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://client-origin"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"));
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type", "Access-Control-Allow-Origin",
            "Access-Control-Allow-Headers", "X-Requested-With", "requestId", "Correlation-Id"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

    @Bean
    public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() 
      return configurer -> 
        List<RequestMatcher> matchers = new ArrayList<>();
        matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
        configurer.requestMatchers(new OrRequestMatcher(matchers));
      ;
    

这适用于所有 GET 请求,当我在浏览器中手动执行其他动词的请求时——除了 OPTIONS。在 TLS 握手期间,预检请求继续失败,并被 Firefox 中止。我很困惑,甚至可以通过(双向 SSL)HTTPS 发送未经身份验证的(无凭据)请求吗?

如何配置 spring security 或 spring boot 以允许未经身份验证的 OPTIONS 请求使用双向 SSL(通过 HTTPS)启用配置?

【问题讨论】:

Chrome 做到了——它将凭据添加到预检选项。 2 路 SSL 配置中未经身份验证的请求是否存在安全风险? 对,我知道 Firefox 只是遵循规范。如果服务器要求,我建议规范允许可选凭据。但我已经接受它不会(至少现在是这样),并且似乎无法让 Spring 允许未经身份验证的 OPTIONS。 对,应该是服务器。我假设服务器在发送 OPTIONS 之前希望在 TLS 握手中获得客户端证书。 Firefox 不发送,TLS 握手失败,Firefox 中止请求。 【参考方案1】:

预检请求应排除凭据,请参阅CORS specification:

7.1.5 带预检的跨域请求

为了保护资源免受在本规范存在之前无法源自某些用户代理的跨域请求,发出预检请求以确保资源了解本规范。此请求的结果存储在预检结果缓存中。

以下步骤描述了用户代理必须为预检的跨域请求执行哪些操作。这是对非同源 URL 的请求,首先需要使用预检结果缓存条目或预检请求进行授权。

[...]

排除用户凭据。

要处理没有凭据(SSL 客户端证书)的预检请求,您必须更改服务器的 SSL 配置。

例如,见Spring Boot Reference Guide:

server.ssl.client-auth= # Whether client authentication is wanted ("want") or needed ("need"). Requires a trust store.

【讨论】:

以上是关于将 Spring Security(双向 SSL)配置为仅对 CORS 预检 OPTIONS 请求进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security with LDAP over SSL:需要更多细节

Spring Security:SSL 加速器后面的 requires-channel="https"

Spring Security 和 Angular 6 HTTPS 请求

Spring Security 不会将 HTTP 重定向到 HTTPS

Spring security @EnableOAuth2Sso - 我们不能跳过开发环境的自签名 ssl 连接

IIS 7.5 与 aws elb 问题 http 到 https j_spring_security_check.action