URL in a JSON payload causes CORS issue

Viewed 242

I have a GraphQL backend with React front-end for an application. The CORS policy is set to only allow the front-end https://my.server:443 to access the backend https://my.server:4444 and it works just fine....most of the time.

My Express.js server is started as follows:

server.start(
  {
    cors: {
      credentials: true,
      origin: [ "https://my.server" ]
    },
  },
  (deets) => {
    console.log(`Server is now running on port http://localhost:${deets.port}`);
  }
);

When I make a POST request to the API with a payload that contains a URL, I get a CORS error. Here is the dialogue:

OPTIONS Request:

OPTIONS / HTTP/2
Host: my.server:4444
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Referer: https://my.server/qualifications
Origin: https://my.server
Connection: keep-alive
TE: Trailers

Response:

HTTP/2 204 No Content
date: Thu, 15 Oct 2020 12:16:25 GMT
x-powered-by: Express
access-control-allow-origin: https://my.server
vary: Origin, Access-Control-Request-Headers
access-control-allow-credentials: true
access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
access-control-allow-headers: content-type
X-Firefox-Spdy: h2

POST Request:

POST / HTTP/2
Host: my.server:4444
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 616
Origin: https://my.server
Referer: https://my.server/qualifications
Connection: keep-alive
Cookie: token=eyJhbGc<removed-jwt>gmWVO_I68
TE: Trailers

with payload:

{
  "operationName": "UPDATE_QUALIFICATION_MUTATION",
  "variables": {
    "id": "ckf54v5lm0dck07821octvj4b",
    "name": "MBA",
    "description": "Testing Links",
    "link": "http://www.google.com"                          <---- URL in payload
  },
  "query": "mutation UPDATE_QUALIFICATION_MUTATION($id: ID!, $name: String, $link: String, $description: String, $company: CompanyUpdateOneInput) {\n  updateQualification(where: {id: $id}, data: {name: $name, link: $link, description: $description, company: $company}) {\n    id\n    name\n    description\n    link\n    company {\n      id\n      name\n      __typename\n    }\n    __typename\n  }\n}\n"
  }

Response:

HTTP/2 403 Forbidden
server: awselb/2.0
date: Thu, 15 Oct 2020 12:16:25 GMT
content-type: text/html
content-length: 118
X-Firefox-Spdy: h2

OKAY...let's try this again without the URL this time...

What confuses the heck out of me is that when I do the SAME THING, with the exception of the one field containing a URL - I get this:

OPTIONS Request:

OPTIONS / HTTP/2
Host: my.server:4444
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Referer: https://my.server/qualifications
Origin: https://my.server
Connection: keep-alive
TE: Trailers

Response:

HTTP/2 204 No Content
date: Thu, 15 Oct 2020 13:07:41 GMT
x-powered-by: Express
access-control-allow-origin: https://my.server
vary: Origin, Access-Control-Request-Headers
access-control-allow-credentials: true
access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
access-control-allow-headers: content-type
X-Firefox-Spdy: h2

POST Request:

POST / HTTP/2
Host: my.server:4444
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/json
Content-Length: 560
Origin: https://my.server
Referer: https://my.server/qualifications
Connection: keep-alive
Cookie: token=eyJhbG<removed-jwt>WVO_I68
TE: Trailers

With payload:

{
  "operationName": "UPDATE_QUALIFICATION_MUTATION",
  "variables": {
    "id": "ckf54v5lm0dck07821octvj4b",
    "name": "MBA",
    "description": "Testing Links",
    "link": "not-a-url"                                <---- THIS CHANGED
  },
  "query": "mutation UPDATE_QUALIFICATION_MUTATION($id: ID!, $name: String, $link: String, $description: String, $company: CompanyUpdateOneInput) {\n  updateQualification(where: {id: $id}, data: {name: $name, link: $link, description: $description, company: $company}) {\n    id\n    name\n    description\n    link\n    company {\n      id\n      name\n      __typename\n    }\n    __typename\n  }\n}\n"
  }

Response:

HTTP/2 200 OK
date: Thu, 15 Oct 2020 13:07:41 GMT
content-type: application/json
content-length: 173
x-powered-by: Express
access-control-allow-origin: https://my.server
vary: Origin
access-control-allow-credentials: true
X-Firefox-Spdy: h2

Now it works fine?? I am left with more questions than anything else:

  • How can the JSON payload of a request affect CORS?
  • Is there something missing from my Express server CORS configuration?
  • Is there some kind of general issue with sending URL's in the payload?
  • If so, is there a suggested workaround? (I was just going to base64 encode/decode it on the front-end, but that seems "wrong" to have to do that!)

Any pointers would be appreciated!

1 Answers

We stumbled upon a similar issue today.

As it turned out, in our case it was caused by WAF configuration that denied the request. We found it out after analysis of WAF sampling and load balancer logs.

Best of luck!

Related