avatarTeri Radichel

Summarize

KMS Keys for Static Website S3 Buckets

ACM.252 Separate keys for logs and content and different admins in different environments

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

⚙️ Check out my series on Automating Cybersecurity Metrics | Code.

🔒 Related Stories: Application Security | Secure Code | AWS Security | S3 | KMS

💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Nothing is every simple if you want it to be secure. But as I go I try to simplify it all. That’s the goal of this whole blog series. In the last post I tried to make it easier to see stack failures from my CloudFormation deployment function.

What I thought was going to be a single line to call my KMS key creation function turned out to be much more involved. So much so that you get a separate blog post to explain why — and how it relates to a recent Microsoft breach resulting from a validation error. We’re going to think through a deployment design and architecture that prevents a similar risk.

Mind you, I am itching to get this bucket deployed as much as anyone, but I know doing it right the first time will save me time in the long run. As my Dad used to say — there’s smart lazy and dumb lazy. Dumb lazy takes short cuts that cost your time (as in the cost of a data breach) and money later when you have to redo everything. Smart lazy does it right the first time.

KMS keys for S3 buckets hosting static web sites?

Initially I considered skipping the KMS key since the content for a public static website is viewable by anyone anyway. However, that complicates enforcement of rules across the organization — or in other words cloud governance.

Although Capital One had a mishap, one of the many things I think they did right was to enforce encryption everywhere. It’s easier to do that than to try to figure out what should and should not be encrypted and to manage exceptions. Managing exceptions can take more time than simply doing the right thing once and automating it for consistency.

In addition, protecting your data with a key that hopefully an attacker cannot access can disallow them from accessing and encrypting the content with their own key (i.e. ransomware.) So I decided to take the time to encrypt the contents.

For the website I’m using in this demonstration, our content may be public. But this static web solution also may be an internal, password-protected static site with corporate reports later.

In addition, we want to encrypt the logs.

Separate KMS keys for every site?

One question is, do we want to use a separate KMS key for every site? That could become pricey. To start I’m going to presume one key for dev, one for QA, and one for prod account apps.

In the future we might create separate keys for separate applications hosting sensitive data in production if we are, let’s say, a bank that makes billions of dollars per year. I explained in prior posts how that can reduce the scope of a breach and did in the case of Capital One.

Separate KMS keys for content and logging

If we use a KMS key for the contents bucket we need to grant CloudFront access to that key. Do we also want to use the same key for logs and allow CloudFront to access those?

I’m going to add one thing to this script — the ability to pass in a separate key for logs or content. They can be the same key and in fact, if the logs key is not set we can use the content key. In a dev environment I might use one key. But in a production environment, I have the option to use a separate key for the logs.

Deploy a key for the dev environment

First, I need to deploy a key for the dev environment websites. I already have KMS functions, but as I started to deploy this I realized I had a problem. My KMS admins were created in a separate account than the one where I’m deploying the key. I can’t reference the stack output for my KMS admins be because the key is in a separate account.

Also, the KMS admin is not really responsible for this key. We’re testing things out in a Sandbox environment where the web admin is creating everything. This is not my long term solution it’s a starting point to get code working.

I started out with this one-off solution:

And a condition:

I’m already using a condition for my principal here:

So I could try to do something like this:

That’s ugly. It’s not a clean solution and if we need to add other environments it gets even messy and hard to maintain. It’s error prone. I tried it and it also creates complications regarding how to get that parameter into the code in a clean way.

I’m going to revise it. Instead of some hacky solution, I decided to add an environment (env) variable to define in which environment the key will be deployed. I already have an org root key admin and what will become my long term solution: KMS administrators. But now I need a Sandbox web account admin to be able to administer keys in a test environment.

I’m going to switch this to use a mapping solution. I’ve shown you how to do that in a prior post. You can pass in a value and that value maps to another vaue that you use in your template.

In other words I could do this:

Environment Value: Root > Role ARN: Orgroot Role
Environment Value: KMS > Role ARN: KMS Key Admin
Environment Value: SandboxWeb > Role ARN: Sandbox Web Admin

I explained before that we have to think about the threat model where someone makes an unauthorized user a key administrator. How can we prevent that?

Here’s the scenario:

Someone is deploying a key to production but they specify the Sandbox environment. The mapping table above sees that the environment variable is Sandbox and makes the Sandbox Admin Role the admin of the production key.

Something similar just happened in a Microsoft breach with failed validation.

In-depth analysis of the Exchange Online activity discovered that in fact the actor was forging Azure AD tokens using an acquired Microsoft account (MSA) consumer signing key. This was made possible by a validation error in Microsoft code. The use of an incorrect key to sign the requests allowed our investigation teams to see all actor access requests which followed this pattern across both our enterprise and consumer systems.

So in other words, Microsoft did not validate that the key used to sign the requests had the appropriate permission in the environment in which it was used.

Limiting unauthorized administrator assignment in our KMS Key CloudFormation Template

We don’t want our Sandbox Admin role to be able to access production systems. How can we prevent that if we are passing in a value that grants the SandBox admin permission to administer the key we are creating?

There’s no one hundred percent foolproof method unless you are using defense in depth and separation of duties in your cloud Architecture and process design. You need to think through your architecture, governance, and processes, not one single piece of code.

But within our KMS key template, I can limit the possibility of unwanted access like this:

  • Pass in the environment parameter (dev, QA, Prod, Root, Sandbox).
  • I can map to the name of the role rather than the full ARN.
  • I can concatenate the role name to the account number where the key is deployed, obtained via the pseudo parameter AWS::AccountId.

I’ve written about pseudo parameters before. Find the documentation here:

Threat modeling — considering the risks associated with our solution

What does that approach do for us? Walk through the scenario I just mentioned.

First, as I mentioned before, simply passing in an ARN would allow someone to make any role a key administrator. That’s why we’re not doing that for key administration. It’s in template. If we wanted to pass in the ARN we would need some mechanism to validate the ARN prior to passing it into the template to make sure it was not manipulated in some way.

Let’s say we map the full ARN to an environment value.

  • Someone is trying to grant the Sandbox Admin permission to administer a production key.
  • They specify the Sandbox environment but they deploy the key to the production account.
  • The code maps the Sandbox web admin role ARN to the environment.
  • The Sandbox web admin role ARN gets assigned as the administrator of a production key.

Consider my approach. What happens when someone tries to do the same thing now?

  • Someone is trying to grant the Sandbox Admin permission to administer a production key.
  • They specify the Sandbox environment but they are trying to deploy to the production account.
  • The role ARN, when concatenated, will have the production account number but the role name will be the Sandbox web admin role name (which should be different than the production role name).
  • That role ARN does not exist in the production account so the deployment will fail.

That’s why I’m going to use the approach of concatenating the ARN rather than mapping the full ARN in my code.

Another risk exists with this approach. Do you recognize it?

If someone can create the role with the same name as the SandBox web admin in production that could be assigned as a production key administrator. They would also need to add a trust policy that allows cross-account access to assume the role. That last risk is addressed with segregation of duties in your overall cloud security architecture and governance.

Revisions to our KMS Key template

I revised the KMS key template to align with the above design as follows.

Recall that my key template is in:

SecurityMetricsAutomation/KMS/stacks/Key/cfn/Key.yaml

I added the environment parameter:

And the mappings:

Then as explained in my prior post on mappings for role names we can use Join to concatenate the values and FindInMap to get the role name.

Now it’s easier to add a new environment and administrator to our template compared to a convoluted If statement. The above code is a bit complex but solid and we don’t need to touch it again for a new environment. We simply add a new environment to the list of allowed values and the map.

Changing the KMS function that deploys a key

Next I need to update the KMS function that deploys a key. For backwards compatibility, I want to default the environment to KMS if an environment is not passed in. You may recall we had some complexities with key descriptions in the past, but I’m going to require that value and the environment value will be optional.

Recall that my key functions are in:

SecurityMetricsAutomation/KMS/stacks/Key/key_functions.sh

I add the new parameter:

Add the code to set the environment to the KMS account by default.

Add the environment parameter to the end of the parameters list.

Add the key deployment to our static website deployment script

First I set the env variable at the top of my script. I may use this for other resources as well.

Then I add the code to deploy the key. In the code below I’m querying the key rather than pull it from the CloudFormation stack. The web admin does not have permission to modify that stack but I granted the web admin permission to query roles in the account. I’m not sure if I will stick with this solution long term. Stay tuned — everything about IAM role deployment will be changing soon so this is my immediate fix for getting websites deployed for testing purposes.

As noted above I’m creating one key for each environment. In this case, my environment is SandboxWeb, so I’m creating a KMS key called SandboxWeb-Apps as you can see in the key alias variable below. I’m going to use that same key for contents and logs for the initial deployment test.

That works. Now I have a key for deploying my static website S3 bucket in the Sandbox Account.

Honestly, thinking through the threat model and architecture and trying to avoid making changes initially took much longer than it did to quickly implement this code.

Note that I cover AWS S3 Bucket KMS keys in a separate post.

I added an AWS S3 Bucket Key to my CloudFormation template in this post:

Next up …the S3 bucket. Finally.

Follow for updates.

Teri Radichel | © 2nd Sight Lab 2023

About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
⭐️ Author: Cybersecurity Books
⭐️ Presentations: Presentations by Teri Radichel
⭐️ Recognition: SANS Award, AWS Security Hero, IANS Faculty
⭐️ Certifications: SANS ~ GSE 240
⭐️ Education: BA Business, Master of Software Engineering, Master of Infosec
⭐️ Company: Penetration Tests, Assessments, Phone Consulting ~ 2nd Sight Lab
Need Help With Cybersecurity, Cloud, or Application Security?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Request a penetration test or security assessment
🔒 Schedule a consulting call
🔒 Cybersecurity Speaker for Presentation
Follow for more stories like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
❤️ Sign Up my Medium Email List
❤️ Twitter: @teriradichel
❤️ LinkedIn: https://www.linkedin.com/in/teriradichel
❤️ Mastodon: @teriradichel@infosec.exchange
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab
Kms
AWS
S3
Static Website
Logs
Recommended from ReadMedium