moffutt
moffutt

Reputation: 19

CORS failing when POST request has no body and server response is a 403 Forbidden error

The application I am working on sends a request from an apache server running on localhost:80 to a tomcat server running on localhost:8080 this is creating a cross origin scenario. When the application sends a POST request with a body the pre-flight request is sent, the response contains all of the headers necessary and then the actual request is sent and it shows success. If I send the same request without a body there is no pre-flight request sent and I receive the

'Access to XMLHttpRequest at 'http://localhost:8080/webapp/api' from origin 'http://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I have added the CORS filter to Tomcat and have it configured:

        <filter-name>CorsFilter</filter-name>
        <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
        <init-param>
          <param-name>cors.allowed.origins</param-name>
          <param-value>http://localhost</param-value>
        </init-param>
        <init-param>
         <param-name>cors.supportedMethods</param-name>
            <param-value>GET, POST, HEAD, PUT, DELETE</param-value>
        </init-param>
        <init-param>
            <param-name>cors.support.credentials</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowed.headers</param-name>
            <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
        </init-param>
        <init-param>
            <param-name>cors.exposed.headers</param-name>
            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
        </init-param>
        <init-param>
            <param-name>cors.preflight.maxage</param-name>
            <param-value>10</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CorsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Here are some fiddler outputs: Without the body in the post

Request

POST http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost/cart-home
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=DF1A170A1B38AD7B2EFDC70A742027B8; hazelcast.sessionId=HZ90788223E83048308A167BE8514D5649; NG_TRANSLATE_LANG_KEY=%22en%22; JSESSIONID=205B812430E4261E2E2999AED6652E1D; liveagent_oref=; liveagent_ptid=4a8955a7-df01-4e22-8f77-16cabf542a11; liveagent_sid=95a17ee7-44da-480e-9f80-0fce17de4ecb; liveagent_vc=3; SESS49960de5880e8c687434170f6476605b=tumGvaRrAVNtDjAwOmMVuNivVnoZLt3muLr8KjAcyj4; ceshopCartUUID=null

Response

HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Content-Type: text/plain;charset=UTF-8
Date: Thu, 05 Dec 2019 22:59:59 GMT
Content-Length: 0 

and with a body

Request

POST http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 15
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Content-Type: application/json;charset=UTF-8
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost/cart-home
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=DF1A170A1B38AD7B2EFDC70A742027B8; hazelcast.sessionId=HZ90788223E83048308A167BE8514D5649; NG_TRANSLATE_LANG_KEY=%22en%22; JSESSIONID=205B812430E4261E2E2999AED6652E1D; liveagent_oref=; liveagent_ptid=4a8955a7-df01-4e22-8f77-16cabf542a11; liveagent_sid=95a17ee7-44da-480e-9f80-0fce17de4ecb; liveagent_vc=3; SESS49960de5880e8c687434170f6476605b=tumGvaRrAVNtDjAwOmMVuNivVnoZLt3muLr8KjAcyj4; ceshopCartUUID=null

{"some":"body"}

Response

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 05 Dec 2019 23:08:34 GMT

b75
{"cartUUID":null}

So, does anyone have any idea why the Allow-Origin-Headers are not being returned when the request has no body, and hence failing.

Upvotes: 1

Views: 3214

Answers (1)

Florian Bruckner
Florian Bruckner

Reputation: 81

Having run into the same problem I did find out that this is actually what the Tomcat CORS filter does. This is the relevant part of CorsFilter:

        CORSRequestType requestType = CORSRequestType.INVALID_CORS;
...
                    } else if ("POST".equals(method)) {
                        String mediaType = getMediaType(request.getContentType());
                        if (mediaType != null) {
                            if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
                                    .contains(mediaType)) {
                                requestType = CORSRequestType.SIMPLE;
                            } else {
                                requestType = CORSRequestType.ACTUAL;
                            }
                        }
                    }

If the method is POST, but there is no Content-Type header, the request type remains INVALID_CORS. A bit down in the filter code this leads to an error code 403.

So, this is what causes the 403, but I am not sure if this is handled correctly here.

As my use case is an application that must be deployed on multiple versions of tomcat, even if this was changed/fixed at some point in time, we would get a dependency on this very recent tomcat version. Something I want to avoid, so I will either package my own version of a CorsFilter with our application or change the API definition so it will send a Content-Type.

Upvotes: 5

Related Questions