首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >春季云网关上的CSRF从POST请求中删除formData 400个错误请求

春季云网关上的CSRF从POST请求中删除formData 400个错误请求
EN

Stack Overflow用户
提问于 2022-07-26 02:40:22
回答 1查看 483关注 0票数 0

我在我的网关服务器上启用了CSRF。我有自己的GUI框架,它通过api网关调用rest服务。

我使用了一个自定义过滤器将CSRF令牌添加到响应头中。

当发出POST电话时,我看到formData丢失了。所以我总是收到400个坏请求错误。

我禁用了CSRF,请求可以顺利进行,没有任何问题。

有什么问题吗?

下面是我的春季云网关配置。网关只用于将请求路由到其他微服务,它没有任何控制器或rest端点。

代码语言:javascript
复制
@SpringBootApplication
public class GatewayApplication {

@Autowired
ProfileManager profileManager;

@PostConstruct
public void onInit() {
    profileManager.printActiveProfiles();
}

public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); }
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    http.authorizeExchange().anyExchange().permitAll();
    http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse());
    return http.build();
   }
}

下面是过滤器代码

代码语言:javascript
复制
@Component
public class CsrfHeaderFilter implements WebFilter {

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    Mono<CsrfToken> token = (Mono<CsrfToken>) exchange.getAttributes().get(CsrfToken.class.getName());
    if (token != null) {
        return token.flatMap(t -> chain.filter(exchange));
    }
    return chain.filter(exchange);
}

}

我的POST rest端点定义为

@RequestParam

下面是来自rest服务端点的代码。它是使用传统servlet springboot框架实现的上游服务。

代码语言:javascript
复制
@RequestMapping(value = "terminate/{listName}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED)
@CrossOrigin
@Loggable (activityname = ActivityLogConstants.DESCRIPTOR_TERMINATE)
public Response terminate(@Context HttpServletRequest reqContext, @PathVariable String listName, @RequestParam(value = "rowData") String rowData)
        throws ServiceException {....}

当请求到达上游服务时,formData就会丢失。

看起来春云网关中的过滤器阻塞了formData

以下是我的netty配置:

代码语言:javascript
复制
@Configuration
public class NettyConfiguration implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

@Value("${server.max-initial-line-length:65536}")
private int maxInitialLingLength;
@Value("${server.max-http-header-size:65536}")
private int maxHttpHeaderSize;

public void customize(NettyReactiveWebServerFactory container) {
    container.addServerCustomizers(
            httpServer -> httpServer.httpRequestDecoder(
                    httpRequestDecoderSpec -> {
                        httpRequestDecoderSpec.maxHeaderSize(maxHttpHeaderSize);
                        httpRequestDecoderSpec.maxInitialLineLength(maxInitialLingLength);
                        return httpRequestDecoderSpec;
                    }
            )
    );
}
}

下面是我的application.yml

样本日志:

2022-07-28 09:18:20.743调试26532 -- ctor-http-nio-5 r.n.http.client.HttpClientOperations : id:199cd714-5,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080接收到响应(自动读取:假):X-内容-类型-选项=nosniff,XSS r.n.http.client.HttpClientOperations=1;mode=block,缓存-控制=无缓存,最大年龄=0,必须重新验证,Pragma=无缓存,Expires=0,严格传输-安全=最大年龄=31536000;includeSubDomains,X框架-选项=否认,X-应用-上下文=应用程序:18080,Date=Thu,7月28日2022 03:48:20 GMT,Connection=close,内容长度=0 2022-07-28 09:18:20.744调试26532 - ctor-http-nio-5 r.n.r.DefaultPooledConnectionProvider :id:199cd 714-5,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080 r.n.r.DefaultPooledConnectionProvider connection=PooledConnection{channel=id: 0x199cd714,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080 },response_received) 2022-07-28 09:18:20.744调试26532 - ctor-http-nio-5 reactor.netty.channel.FluxReceive : id:199cd714-5,L:/127.0.0.1:50342 -R:localhost/127.0.0.1:18080 FluxReceive{pending=0,cancelled=false,inboundDone=false,inboundError=null}:订阅入站接收器2022-07-28 09:18:20.744调试26532 - ctor- HTTP -nio-5 r.n.http.client.HttpClientOperations :id:199cd 714-5,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080最后收到的HTTP2022-07-28:18:20.744调试26532 - ctor-http-nio-5 r.n.http.server.HttpServerOperations : id:b0f975eb-11,L:0:0:0:0:0:0:0:1:10443-R:/0:0:0:0:0:0:0:0:1:50337正在减少待决响应,现在0 2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 r.n.http.server.HttpServerOperations : id:b0f975eb-11,L:0:0:0:0:0:0:0:1:10443- R:/0:0:0:0:0:0:0:1:50337发送了最后的HTTP数据包,终止了通道2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 o.s.w.s.adapter.HttpWebHandlerAdapter : b0f975eb-11,L:0:0:0:0:0:0:0:1:10443- R:/0:0:0:0:0:0:0:1:50337 BAD_REQUEST 2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 r.n.http.server.HttpServerOperations : id:b0f975eb-11,L:0:0:0:0:0:0:0:1- R:/0:0:0:0:0:0:0:1:50337最后的HTTP响应帧2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 c.m.webgateway.handler.RequestLogger :处理/cms-service/webapi/终止/请求60055 2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 r.n.r.DefaultPooledConnectionProvider : id:199cd714,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080 onStateChange(POST{uri=/cms-service/webapi/terminate/descriptor,connection=PooledConnection{channel=id: 0x199cd714,L:/127.0.0.1:50342 -R:localhost/127.0.0.1:18080},response_completed) 2022-07-28 09:18:20.745调试26532 - ctor-http-nio-5 r.n.r.DefaultPooledConnectionProvider :id:199cd 714,L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080 onStateChange(POST{uri=/cms-service/webapi/terminate/descriptor,connection=PooledConnection{channel=id: 0x199cd714,L:/127.0.0.1:50342 -R:localhost/127.0.0.1:18080},断开) 2022-07-28 09:18:20.752调试26532 - ctor-http-nio-5 r.n.resources.PooledConnectionProvider :id:199cd 714,L:/127.0.0.1:50342!R:localhost/127.0.0.1:18080通道关闭,现在:0个活动连接,4个非活动连接和0个挂起的获取请求。2022-07-28 09:18:20.752调试26532 - ctor-http-nio-5 r.n.r.DefaultPooledConnectionProvider :id:199cd 714,L:/127.0.0.1:50342!R:localhost/127.0.0.1:18080 onStateChange(PooledConnection{channel=id: 0x199cd714,L:/127.0.0.1:50342!)R:localhost/127.0.0.1:18080},断开) 2022-07-28 09:18:23.805 DEBUG 26532 -- ctor-http-nio-5 r.n.http.server.HttpServerOperations : id:b0f975eb,L:/0:0:0:0:0:0:0:1:10443 -R:0:0:0:0:0:0:0:0:1:50337现在12022-07-28 09:18:23.805调试26532 - ctor-http-nio-5 reactor.netty.http.server.HttpServer : id:b0f975eb-12,L:0:0:0:0:0:0:0:1:10443- R:/0:0:0:0:0:0:0:1:50337处理程序正在应用: org.springframework.http.server.reactive.ReactorHttpHandlerAdapter@7c82616c 2022-07-28 09:18:23.805调试26532 - ctor-http-nio-5 o.s.w.s.adapter.HttpWebHandlerAdapter : b0f975eb-12,L:0:0:0:0:0:0:0:1:10443-R:/0:0:0:0:0:0:0:0:0:0:1:50337 HTTP“/cms-service/webapi/data/描述符”

下面是指向示例项目的链接。https://github.com/manjosh1990/webgateway-issues

我试图忽略表单URL编码的请求和获取请求,但它仍然不能工作

代码语言:javascript
复制
private static final Set<HttpMethod> ALLOWED_METHODS = new HashSet<>(
        Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE, HttpMethod.OPTIONS));
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http.authorizeExchange().anyExchange().permitAll().and()
            .csrf(csrf -> csrf
                    .requireCsrfProtectionMatcher(ignoringFormUrlEncodedContentType())
                    .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()));
    return http.build();
}
private ServerWebExchangeMatcher ignoringFormUrlEncodedContentType() {
    return (exchange) -> !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(
            exchange.getRequest().getHeaders().getContentType()) || !ALLOWED_METHODS.contains(exchange.getRequest().getMethod())
            ? ServerWebExchangeMatcher.MatchResult.match()
            : ServerWebExchangeMatcher.MatchResult.notMatch();
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-10 19:17:42

谢谢你的最小样本来重现这个问题!

经过一些测试后,我无法为您的配置提供一个解决方案或修复,从而允许表单post (URL编码)通过启用CSRF保护的网关。我最好的猜测是,它与Security是如何使用请求体(应该缓存以供后续过滤器使用)和如何使用请求体来代理下游服务有关。

我通过禁用CSRF保护和添加以下过滤器对此进行了测试:

代码语言:javascript
复制
@Component
public class TestWebFilter implements WebFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        return Mono.defer(() -> exchange.getFormData()
                .doOnSuccess(System.out::println))
                .then(chain.filter(exchange));
    }
}

在我的测试中,这会导致通过网关的请求在接收到以下信息之前阻塞很长时间:

代码语言:javascript
复制
{
    "timestamp": "2022-08-10T19:13:54.265+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/cms-service/webapi/service/post/test"
}

由于这似乎是Security中的一个bug,我建议在Security中提交一个bug,我们可以从那里着手处理。

如果您同时想解决这个问题,您可以禁用这些类型请求的CSRF保护,如下所示:

代码语言:javascript
复制
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange((authorize) -> authorize
                .anyExchange().authenticated()
            )
            .csrf((csrf) -> csrf
                .requireCsrfProtectionMatcher(ignoringFormUrlEncodedContentType())
                .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
            )
            .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
        return http.build();
    }

    private ServerWebExchangeMatcher ignoringFormUrlEncodedContentType() {
        return (exchange) -> !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(
            exchange.getRequest().getHeaders().getContentType())
                ? ServerWebExchangeMatcher.MatchResult.match()
                : ServerWebExchangeMatcher.MatchResult.notMatch();
    }

}

的重要性:--这并不理想,因为这些请求不会受到保护。但是,如果从未在浏览器中执行这些请求,这可能是有意义的。在这种情况下,有一个单独的身份验证机制是有意义的,例如需要一个承载令牌而不是表单登录等等(如上面的示例所示)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73117195

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档