-
Notifications
You must be signed in to change notification settings - Fork 38.4k
Consider not rejecting preflight requests when no CORS configuration is provided #31839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hello @rcolombo - unfortunately, there isn't enough information here to help you. I'm not seeing any specific CORS-related change in the 6.1.x line. Can you given an example of request that is being rejected? You're also mentioning a custom CORS interceptor and we're not seeing it here. Can you share a minimal sample application that reproduces the problem? |
You could place a debug point in |
Perhaps this is user error. Thank you for pointing me to the This line rejects my request because I have a preflight request but a null config: https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java#L85 I'm still unclear why I didn't see this until version 3.2.0 as the The fix that worked for my exact use case was to implement my CORS Interceptor as a CORS Filter instead. This way, I am able to add CORS response headers before My overall goal is to skip/disable the built-in Spring CORS Processor, as my exact use case requires more complicated processing. |
Based on your feedback, I close this issue. If you want to skip Spring processing, putting a custom CORS filter will indeed likely disable Spring own CORS processing if it sets CORS response headers. Be aware you also could probably instead provide your own |
@sdeleuze @rcolombo this is a valid issue even when original poster decided to move away from built-in processor. The issue stems from https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java#L96-104:
where when one does not supply the config, it is assumed that CORS is not enabled and hence you return true. However the block above create a situation where you reject the pre-flight even when config is null. You end up having users reporting issues that only a combination of headers in pre-flight would trigger this, like this here: spring-cloud/spring-cloud-gateway#112. For my use case, I'd like pre-flight AND actual calls to all route to target server in Spring Cloud Gateway, and the expectation is that I can disable pre-flight rejection here as well. When I start supplying a config to make this work, I ended up having CORS check both at my gateway AND my target app, resulting duplicated As it currently stands, I also end up rolling my own CorsFilter to allow pre-flight at gateway only, and letting target servers to handle actual. |
I had a very similar problem: I'm migrating a huge legacy app (20+ years of development ;) ) to using Spring Web bit by bit. I ended up implementing a ServletFilter to kind of hide preflight requests from Spring by wrapping the respective request and disguising it as a simple GET to avoid any of Spring Web's logic to trigger.
<filter>
<filter-name>hideOptionRequestFromSpringFilter</filter-name>
<filter-class>HideOptionRequestFromSpringFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hideOptionRequestFromSpringFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* Hides the Origin header from Spring to skip its CORS processing.
*/
public class HideOptionRequestFromSpringFilter implements Filter {
public final static String HIDE_ORIGIN_FROM_SPRING_ATTRIBUTE = "xxx.SkipSpringCorsProcessorFilter.Origin";
public final static String HIDE_METHOD_FROM_SPRING_ATTRIBUTE = "xxx.SkipSpringCorsProcessorFilter.Method";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
UserBean.detachFromCurrentThread();
final HttpServletRequest httpRequest = (HttpServletRequest) request;
if (HttpMethod.OPTIONS.matches(httpRequest.getMethod())) {
httpRequest.setAttribute(HIDE_ORIGIN_FROM_SPRING_ATTRIBUTE, httpRequest.getHeader(HttpHeaders.ORIGIN));
httpRequest.setAttribute(HIDE_METHOD_FROM_SPRING_ATTRIBUTE, httpRequest.getMethod());
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpRequest) {
@Override
public String getHeader(String name) {
if (HttpHeaders.ORIGIN.equalsIgnoreCase(name)) {
return null;
}
return super.getHeader(name);
}
@Override
public String getMethod() {
return "GET";
}
};
chain.doFilter(wrapper, response);
} else {
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
@RestController
public class RestService {
/**
* Handle multi-part requests.
*/
@RequestMapping(path = "/rest/**")
protected void service(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
// Unwrap original Request; Counterpart to HideOptionRequestFromSpringFilter
if (request instanceof HttpServletRequestWrapper) {
final HttpServletRequestWrapper w = (HttpServletRequestWrapper) request;
final HttpServletRequest wrappedRequest = (HttpServletRequest) w.getRequest();
if (HttpMethod.GET.matches(request.getMethod()) && HttpMethod.OPTIONS.matches(wrappedRequest.getMethod())) {
request = wrappedRequest;
}
}
// Handle request in our very own way...
} Maybe in a next step we'll try to adopt Spring Web's handling of preflight requests into our custom (and scriptable) way of handling them. Won't be an easy task, thou. |
Maybe rejecting preflight requests when there is no CORS config is indeed too strong, as the lack of proper response header could be enough to get the right default behavior, and it could improve consistency between preflight/non-preflight request and help non-builtin CORS support like the use cases described above. I will tentatively see if I can provide a reasonable solution for 6.2.x (otherwise, I may target 7.0.x). |
After discussing with @rstoyanchev, we decided to target Spring Framework 7.0 as there is a workaround and we prefer to get early feedback on this change, even if in theory that should be fine. |
Affects: 3.2.0
My project is now getting "403 Invalid CORS Request" responses when sending an OPTIONS request with the "access-control-request-method" header. It worked fine in version 3.1.6.
"access-control-request-method" is a valid CORS header so I'm not sure why this error is surfacing. Two other CORS headers that I use are "origin" and "access-control-request-headers" and they cause no issues. I can send any combination of those two headers and I get the correct responses.
One way around this is to add a custom CorsMapping like such:
However, I don't want to do this as I have my own custom CORSInterceptor that I want to handle OPTIONS requests. The above solution does things I don't want, such as setting the response header of access-control-allow-origin: "*"
Did something change that specifically causes issues with just this header?
The text was updated successfully, but these errors were encountered: