Policies and Procedures for Secure Access

From AWS Security by Dylan Shields

Manning Publications
12 min readMay 18, 2023

--

Photo by KeepCoding on Unsplash

This article deals with securing access to your AWS account.

Applying Least Privilege Access Control

The Principle of Least Privilege is the idea that you should grant the fewest permissions needed to get the job done. You can think of it like safety deposit boxes at a bank. If a customer pays for a box, they get a key that only opens that box. If a customer purchases ten boxes, they get ten different keys. This is least privilege. The customer has access to the boxes they need (the ones they purchased), and never has access to ones they don’t need. The alternative, breaking least privilege, usually happens when maintaining least privilege is hard or inconvenient. Imagine if the customer with ten boxes complained to you that they could never remember which key opened which box. You might give them a master key that opens all the boxes. Now the customer doesn’t have to worry about which key opens each box, but they can also open everyone else’s boxes.

The idea underpinning the principle of least privilege is that every permission granted is a liability. If a user has permission to delete production database tables, then you’re at risk of that user deleting those tables. It could happen by accident. That user might mistakenly type the wrong command and bring everything down. One of the most prominent outages at AWS was caused by an event like this, where a simple mistake brought down the Amazon S3 service for over an hour. Accidents aren’t the only risk; an attacker could gain access to that user’s credentials and delete the tables on purpose. Maybe a bit more unlikely, but the user could be an attacker, an insider threat, and delete the tables on purpose. The most secure thing to do is to not grant any permissions, but people need access to resources in order to perform their jobs, and the next best thing is to only grant the permissions which are absolutely necessary. This is the principle of least privilege.

Applying least privilege to your IAM policies is the most secure way to grant the permissions necessary to run your application or service. On the other end of the spectrum, giving everyone administrator privileges is the most convenient way to grant the necessary permissions. In practice you’ll likely find yourself not on either end, but somewhere in the middle of that spectrum. Where you fall on the tradeoff between security and convenience depends on the needs of your organization. The rest of this section presents some of the tradeoffs you might face, and when you want to make them.

Why Least Privilege is Hard

Strictly implementing least privilege in IAM is difficult for three primary reasons. First, it’s hard to write policies to match the exact permissions a user needs. Second, you don’t always know what permissions are needed. Finally, it’s nearly impossible to enforce. It’s important to understand where applying least privilege can cause trouble to give context on when you might want to relax it. Let’s go over each of those reasons in greater detail.

POLICIES ARE HARD TO WRITE

Consider an organization which has developers working on multiple applications in a single account. Let’s think about the policies that one of the developers might need. If the product they work on runs on EC2, they’ll probably need permission to create and delete instances, but what goes in the resource block? We could put ‘*’, but that would grant them permission to delete instances for other products, which they don’t need. We could put in the list of instances which are part of the product, but then we need to make sure the policy gets updated every time an instance is created or destroyed. We could use an attribute-based access control strategy, but then you need to ensure that the correct tags are always applied. None of these solutions are easy, like when you’re trying to apply this to an existing product.

YOU DON’T ALWAYS KNOW WHAT IS NECESSARY

Suppose a user is prototyping a new application. She plans on using EC2, and you grant permissions on that service. Later she decides to use ECS, and you need to grant ECS permissions and remove the EC2 ones. Still later, she changes the compute environment to Lambda, and you must update permissions again. This is going to happen often — it’s the nature of prototyping — and every time something changes, she’ll be blocked waiting on an administrator to update the permissions. Other times the use-case might be clear, but the exact AWS actions needed aren’t. You could have an application that updates items in a DynamoDB table. Would you grant UpdateItem or PutItem permissions? It depends on whether you want partial updates or full replacements of items. Decisions like that can be hard to get right ahead of time, and lead to authorization exceptions when it changes.

IT’S HARD TO ENFORCE

Some best practices are straightforward to enforce, but not strict least privilege access control. If you want to enforce MFA for all users, you can list all of the users in your account and look whether each one has MFA enabled for console access. How would you enforce least privilege for all IAM users? You need to compare the permissions for each user with the permissions they need. Getting all of the permissions for a user requires looking in several places. Getting all of the permissions that a user needs is a much more difficult task. This could require code reviews, talking to users, looking at logs, and inspecting resources. It’s a time-consuming process, and even then it’s hard to be certain that you haven’t missed an action or a resource.

Policy Wildcards

If you want to create an IAM best practice that encourages least privilege access control, you should consider banning wildcards in IAM policies. Policy wildcards are asterisks in policy documents that match a large number of services, actions, or resources. Consider the policy below:

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "ddb:*", #A
"Resource": "*" #B
}]
}

#A First wildcard — Grants all DDB actions.

#B Second wildcard — Grants access to all resources.

In this case there are two wildcards. One that matches all actions in the DynamoDB service, and one that matches all resources (i.e. all DynamoDB resources). This policy is excessive from a least privilege perspective. Does the user with this permission need access to every DynamoDB action? Table 1 shows all thirty-six actions that are matched by this policy.

Table 1. All of the actions matched by “ddb:*”

Although a user might need many of the actions, few users use all of the less common features like global tables, transactions, and continuous backups in DynamoDB. In most cases it isn’t necessary, and we can get closer to least privilege by removing the wildcard and explicitly listing all of the needed actions.

Going back to the second wildcard in that policy, does the user need access to all DynamoDB resources? If not, then least privilege isn’t being followed. Consider not only the current resources, but also the resources that may be created in the near future. Think about whether there’s a reason that the user needs access to all tables, and not only the current tables. The difference is important when using wildcards. An auditor who checks that all tables have backups enabled needs access to all tables, and in that case using a wildcard makes sense. An engineer who works on a tool that queries a few tables, which happen to be the only tables, isn’t a good case for wildcards. The reason is that you might create a new table which the engineer doesn’t need access to, and you’ll no longer be adhering to least privilege. You aren’t going to go through all of your policies every time you create a new resource to check whether your assumptions for wildcards still hold. For that reason, it’s better to explicitly list the tables which are needed in the policy, rather than a wildcard. Also remember that you can use Attribute-Based Access Control (ABAC) via tags if the resources that you need access to are subject to change.

Banning wildcards in IAM policies has its downsides. The first is that there are some valid use cases for wildcards, like the auditor we discussed. If you’re not using wildcards then you need to update the policy every time you create a new resource. You could use ABAC instead, but you still need to tag each new resource when you create it. Another option is to evaluate wildcard use on a case-by-case basis to make sure that it’s only used when necessary. None of those options are as easy and clean as allowing wildcards.

A second downside is that the policies get a lot longer. The example shown below allows all DynamoDB actions on all resources, which is only seven lines.

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "ddb:*", #A
"Resource": "*" #B
}]
}

#A First wildcard — Grants all DDB actions.

#B Second wildcard — Grants access to all resources.

When policies are short, they’re easier to read, review, and understand. If you don’t use wildcards, an equivalent policy could be fifty lines long. At that length it’s a lot harder to understand the policy, and to check that it’s correct. To put it another way, you can explain what the policy in Listing 1.1 does in one sentence. A fifty-line policy granting only a subset of those permissions is much harder to describe.

Along the same lines, policies without wildcards are harder to write. Even in the case where you only need permission for a single action on a single resource, there’s still some added work. First, you probably don’t know the ARN of the resource off-hand. Finding that often involves a trip to the Management Console for that resource’s service. If you’re using AWS CloudFormation, the service that provisions resources based on templates, to write the policy, and the resource is in a different template, requiring you to go through the process of exporting the ARN in the resource’s template, and importing it into the policy’s template. Then there’s the service and action name. These names are case-sensitive, but they don’t match the cases used in the AWS clients. The service names should be all lowercase using the service short name, and the action names should be in PascalCase. The best way to check that you have these written correctly is to use the built-in policy validator in the IAM Console.

This is for a simple policy. More complex policies come with their own challenges. Sometimes you don’t even know which actions are necessary. For instance, which actions are necessary to open the EC2 Console? You need a set of actions granted, mostly describe actions, or else you’ll get an error when trying to load the page. Other times you might be using a library that wraps an AWS service, like an object mapper for DynamoDB. In this case you might not know which actions are being called by the library. Trying to figure out exactly which actions you need permission for can be a frustrating experience of trial and error.

Banning wildcards is a common, yet controversial, best practice. The security merits of the policy are straightforward. Wildcards frequently grant more permissions than necessary and typically go against the principle of least privilege, but wildcards make policies simpler and easier to write. In the context of your organization, consider whether the security benefits of banning wildcards outweigh the loss of convenience.

AWS Managed Policies

In a similar vein to policy wildcards, AWS Managed Policies also goes against least privilege. Recall that AWS Managed Policies are a large set of policies created by AWS which anyone can use. These include policies like the AmazonEC2FullAccess policy. As is suggested by the name, this policy grants access to all actions on all resources in EC2. This is similar to the policy the first code snippet, which granted similar permissions for DynamoDB. This AmazonEC2FullAccess policy is bad for the same reasons. Roughly 350 actions can be done in the EC2 service. No user needs permission to call all of them. If you’re following the principle of least privilege you should create your own policy which only grants permissions to the subset of actions that you need.

Again though, AWS Managed Policies provide a lot of convenience. Like policy wildcards, AWS Managed Policies make writing policies faster, and they’re easy to understand. In addition, they’re also unlikely to have errors. If you write your own policies then you could misspell a service or action name and not grant all the permissions you need. Or you could copy-and-paste from another policy, forget to change something, and end up granting permissions you didn’t expect. A final benefit of AWS Managed Policies is that they get updated when new actions are added. If you have the AWSSecurityHubReadOnlyAccess policy, and AWS Security Hub adds new readonly actions, you don’t need to update anything to get access to those new actions. Because changes are published to the AWS SDK almost daily, this can be pretty useful.

You could create a best practice that bans the use of AWS Managed Policies. Whether this is a good idea depends on the needs of your organization. The key point to think about is how much security value is added, versus how much convenience is lost. Most of these factors are highly dependent on your situation. For example, we said that it’s more work to create your own policy. What if you’re attaching the same policy to a large number of users? In that case, on a per-user basis, it isn’t much additional work. For these reasons, quantifying security value and convenience is difficult, but we can make some comparisons. Relative to banning policy wildcards, banning AWS Managed Policies tends to provide less security value, as at least the policies are written by AWS and they do what you expect. Most are scoped-down for common use cases. In terms of convenience, the Managed Policies provide more value as you don’t even need to write a policy at all. Table 2 below summarizes the major pros and cons of banning managed policies.

Table 2. Summary of Banning AWS Managed Policies as a Best Practice

Shared Permissions (Groups and Managed Policies)

This next best practice takes the principles of the previous two to an extreme — banning any shared permissions. This means not creating any groups or managed policies, and ensuring that roles can only be accessed by a single user. The idea behind the policy is that two users probably don’t need exactly the same permissions. Also, if one user needs a new permission, and you add it to a group or managed policy, then you might have granted that permission to another user who didn’t need it. By crafting a new policy for every user, you can be sure that you’re getting a minimal set of permissions tailored to that user’s needs.

Obviously, this would be a ton of extra work for little security benefit, at least not a lot compared to banning wildcards. One way to compare these practices is by plotting them on a graph with security value on one axis, and convenience cost on the other, like in figure 1.

Figure 1. Graphing Security Value vs Convenience Cost to compare best practices.

In the top left corner are practices with high value and low cost. In the bottom-right corner are practices with low value and high cost. From this we can come up with a simple exercise to help determine which policies you should implement at your organization. Start by taking all of your potential policies and plotting them on the security-convenience graph based on how much value you think it provides versus how much work it adds. Then look at the bottom-left and top-right corners. These should be the easiest decisions to make; work your way towards the center. Eventually you should be able to find a line through the graph where you implement the policies which are above and to the left of the line (high-value, low-cost), and you pass on the practices which are below and to the right (low-value, high-cost). This method isn’t perfect, but if you don’t know where to start then this can be a good way to get the ball rolling. You can also keep this graph for future reference when you evaluate your best practices or consider adding new ones.

That’s all for this article. If you want to learn more about the book, check it out here.

--

--

Manning Publications

Follow Manning Publications on Medium for free content and exclusive discounts.