Score:1

CloudFront CORS request using signed cookies and withCredentials, not sending back Access-Control-Allow-Credentials unless I include some extra header

cn flag

I'm having a very strange issue that I can't seem to crack. I configured a private CloudFront distribution to serve content from a private S3 bucket. I am using signed cookies to grant access to the files. I am also making cross origin requests from a browser for the files, so I need to allow credentials to send the cookie. I configured a custom response headers policy to do this (I set it to set Access-Control-Allow-Credentials to true, explicitly set Access-Control-Allow-Origin to my intended domain, and set Access-Control-Allow-Methods / Access-Control-Max-Age appropriately, and it is set to origin override), and I also set up a custom cache policy to cache based on origin and access-control headers.

this cURL command is not giving the correct response:

curl -v -H "origin: https://my-subdomain.my-domain.com" -H "cookie: CloudFront-Key-Pair-Id=MyKeyPairID; CloudFront-Policy=Base64EncodedPolicy; CloudFront-Signature=SignedPolicy" https://my-other-subdomain.my-domain.com/key/to/my/private/file.txt

it yields the following:

< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
< Content-Length: 576
< Connection: keep-alive
< Date: Fri, 18 Feb 2022 18:34:09 GMT
< Last-Modified: Thu, 16 Dec 2021 14:45:12 GMT
< ETag: "a50884915242f9876bea4bb633963191"
< Accept-Ranges: bytes
< Server: AmazonS3
< Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
< Access-Control-Allow-Origin: https://my-subdomain.my-domain.com
< Vary: Origin
< X-Cache: Hit from cloudfront
< Via: 1.1 redacted.cloudfront.net (CloudFront)
< X-Amz-Cf-Pop: EWR50-C1
< X-Amz-Cf-Id: JxMbPWHeQr0a9AAlf9PI5ksF6xGKVWL1LvpEC9XEoR_PVuVgiJ5zGA==
< Age: 626

Notice the missing Access-Control-Allow-Credentials header.

However, this command, yields the correct response:

curl -v -H "X-some-header: nonsense" -H "origin: https://my-subdomain.my-domain.com" -H "cookie: CloudFront-Key-Pair-Id=MyKeyPairID; CloudFront-Policy=Base64EncodedPolicy; CloudFront-Signature=SignedPolicy" https://my-other-subdomain.my-domain.com/key/to/my/private/file.txt

returns:

< HTTP/1.1 200 OK
< Content-Type: application/octet-stream
< Content-Length: 576
< Connection: keep-alive
< Date: Fri, 18 Feb 2022 18:34:09 GMT
< Last-Modified: Thu, 16 Dec 2021 14:45:12 GMT
< ETag: "a50884915242f9876bea4bb633963191"
< Accept-Ranges: bytes
< Server: AmazonS3
< Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Origin: https://my-subdomain.my-domain.com
< Vary: Origin
< X-Cache: Hit from cloudfront
< Via: 1.1 redacted.cloudfront.net (CloudFront)
< X-Amz-Cf-Pop: EWR50-C1
< X-Amz-Cf-Id: pUSouCDwLH5Zu6-NBUZqKrb5kY407GLqXXtH4EK2-Th0Z9zZNb54ag==
< Age: 693

this time, with the correct Access-Control-Allow-Credentials header. I have no idea what I might have misconfigured to cause this or why this could be happening. Any insights would be greatly appreciated, any configuration or test output needed, just let me know.

Thank you

EDIT:

After some trial and error, I've determined the origin override setting on the Response Header Policy is causing the problem. When that is set to true, it will not send the Access-Control-Allow-Credentials header unless you send some extraneous header with your request. This is an issue as it also causes unwanted preflight requests in the browser.

Turning that setting off and then configuring my S3 Bucket's CORS to look like the below fixed it:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "https://*",
            "http://*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

However, I'm still curious if I was misunderstanding the origin override setting and there's a way to do that correctly, or if this is a bug of some kind in CloudFront

EDIT 2:

Origin Request Policy: AWS Managed CORS-S3Oriign (I tried with this and with no policy, same result)

Cache Policy: Custom Policy to cache based on Origin and access control headers, also tried with standard managed CacheOptimized policy, and the NoCache policy to make sure I wasn't having some issue with non credentialed requests getting stuck in the cache. Also tried invalidating the cache manually and seeing if hits or misses made a difference, they do not.

CF Cache Policy

Response Headers Policy: Custom to allow credentials, this is the original configuration. I eventually set origin override to false, and things started working if I reconfigured my S3 CORS policy to set the headers. I have a random value under Access-Control-Allow-Headers because I was not allowed to leave that field blank for whatever reason. The random header sent does not have to match the header set here to get the credentials header to get returned, but it does have to match for the browsers preflight check to pass. I also did some fiddling with the expose headers setting, nothing helped.

Further note, once I had S3 setting the CORS headers correctly, I was able to remove the Response headers policy entirely, but I did have to keep the custom cache policy or else different origins could get the wrong headers. This is also less than ideal as I will have users accessing these files from different origins, and I believe that if the response headers policy worked correctly, it would be setting the headers after it's pulled from the cache rather than caching the headers (but I may be wrong on that). Seems my only other option is some CF function running on the responses, but that incurs additional cost and overhead, while a functioning response headers policy would be free and more efficient.

But what's very strange is that even if S3 is setting the CORS headers correctly, if I use the Response Headers Policy with origin override true, it still breaks the response without the random header attached.

Response Headers Policy

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.