Cloudfront Shows KMS Details in HTTP Response Headers
By ITSorcerer
It is not surprise this site is built using Cloudfront and S3 but in trying to align with best practices, our S3 bucket isn’t public and our bucket is encrypted via S3 (SSE KMS-CMK). While this type of a configuration may not be common for most (this bucket does contain public data) we opted to enable encryption for a bit of added security as well as mimic typical enterprise security standards. Additionally, on a personal level, adding encryption to the S3 bucket not only adds another layer of protection of our data but if one of us inadvertnetly messes up an S3 policy someone still can’t read the raw data as they would also need access to use the KMS Key.
This brings us to another point, in order to allow Cloudfront to decrypt the data in S3 it too must have access to the KMS Key, without this Cloudfront would not be able to read data from the S3 bucket. To resolve this, we can leverage Lambda @ Edge. Details can be found in this AWS blog post.
While working on this blog we came across something interesting. As we were working through optimizing the site and reducing file sizes and measuring load times, we ended up reviewing response headers from our servers. Imagine our surprise when we realized that our AWS encryption key’s ARN (similar to a GUID) and a few details about our encryption standards were exposed in these response headers.
Let’s dive in a little, we can start by inspecting the Response Headers for any object stored in my S3 bucket it clearly highlights that the bucket is KMS SSE encrypted and also provides the ARN of the KMS Key.
Now, to be clear, these items on their own DO NOT pose a security risk! This is because despite knowing which key is protecting the data in our bucket, there is a key policy that determines who can access utilize the key. With an appropriate key policy in place there is no risk. Regardless, we think leaving these headers in place is making it far too easy should we one day make a mistake on the key policy.
It is also important to highlight the way that KMS works the key itself never actually leaves the KMS platform. The key policy defines who can use the key and for which functions. However should someone be able to use the key for “decrypt” that would allow them to decrypt the data in the bucket, not what we want.
Let’s take a look at what we found and how we fixed it!
Below are the Response Headers we saw when we built the blog:
`date: Sat, 01 Jan 2022 14:58:01 GMT
etag: "be658042c66a5f7939961a2279704a55"
server: AmazonS3
via: 1.1 8b91488fa62e73ed6328bc389e6d1cbe.cloudfront.net (CloudFront)
x-amz-cf-id: 1mVTgDfCfKsGy5FHJKZtj4vNaj-LiXrWsgB8wFVi8NrbkeDwlE7rjw==
x-amz-cf-pop: IAD79-C3
x-amz-server-side-encryption: aws:kms
x-amz-server-side-encryption-aws-kms-key-id: arn:aws:kms:us-east-1:007690XXXXXX:key/af825a22-1d1f-4ac5-a12e-b45ee23XXXXX
x-amz-version-id: 2onoy6cy9zYPmu04CBt6Ur43accQ26Uo
x-cache: Hit from cloudfront`
We understand that disclosure of the key ARN itself is not an immediate security issue however we’ve provided a nefarious individual information that they otherwise may not have had to now attempt to compromise the key. Further to this the account ID is also now disclosed allowing someone to focus their attack attempts.
You should ensure you review your KMS Key policy to ensure that only appropriate permissions are in place.
How we fixed it:
In order to reduce this issue we leveraged a Cloudfront function to remove the headers. Cloudfront Function - Viewer Response:
function handler(event) {
var response = event.response;
var headers = response.headers;
// Set HTTP security headers
// Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation
// We will delete the server-side-encryption headers
delete headers ['x-amz-server-side-encryption'];
delete headers ['x-amz-server-side-encryption-aws-kms-key-id'];
// Return the response to viewers
return response;
}
As you can see from the above, we are removing the offending headers.
Resulting Response Headers after applying Cloudfront Function:
`age: 305
date: Sat, 01 Jan 2022 14:39:03 GMT
etag: W/"2b892d8f1d00d6ab7f2819bd285599bd"
server: AmazonS3
vary: Accept-Encoding
via: 1.1 1299a022d10cdc620f209ba0440a48e9.cloudfront.net (CloudFront)
x-amz-cf-id: aTc5vGotCGzqWfLF_guHYa0Detb27y7c9OtJa-LHAfCPXc3YZD09qw==
x-amz-cf-pop: IAD79-C3
x-amz-version-id: CMJs1L.PFSbuKeGbhRLvEV0tLTUvosLv
x-cache: Hit from cloudfront`
As you can see, after deploying the function the encryption headers are no longer included in the response. Hopefully this won’t take hours for you to resolve!