I had something of an epiphany recently about how to look at permissions and roles in serverless applications. Maybe to some of you, this won’t be as “Soylent Green is People” as it was to me. If so, kudos. If not, let me open your eyes.
Let’s start with some housekeeping. IAM (Identity & Access Management) is the idea that actors in a system like a cloud account have a limited set of permissions, and this scopes what they can do. While it starts out as a concept for defining what your users can do, it quickly gets co-opted to scope the capabilities of other things as well.
For example, if you spin up an EC2 instance in the AWS cloud environment, you need to assign an IAM role to that instance. AWS will enforce that role for you, ensuring that regardless of what logic finds its way into that instance, it will not be able to bypass those restrictions. The same is true for ECS containers, Fargate containers, and Lambda functions. Each has a role that defines what it can and can’t do.
Once Upon a Time…
IAM roles were not an AppSec thing, historically. By that I mean, you didn’t really think to yourself, “what do I do to prevent those pesky OWASP A1 injection attacks? I know, I will configure an IAM role.” IAM roles were an infrastructure thing. They were used to broadly restrict certain evils (or they weren’t), but they had very little to do with what your developers wrote in code, other than that they had better not get in the way. Want proof? It took a while until the cloud providers let you assign a unique role to each serverless function. Some still don’t (yes, Google, I am looking at you). That’s because if all you want from workload IAM roles is to carve out areas you don’t want to touch, you might be satisfied doing that for all the workloads in one common policy.
Let There Be Functions…
For a bunch of interesting reasons, the shift to cloud native development, that is, the idea that you build your applications for the cloud from day one, has also brought in a new affinity for small micro-services. For etymological evidence, track the sentiment associated with the term nano-service. Two years ago, it was a term synonymous with a failed transition to cloud, as in “careful you don’t go overboard and end up with nano-services”. Today it is the design choice de jour.
An interesting side effect of this transition into far finer-grained microservices is the opportunity to assign a separate IAM role to each small function. I’ve written about this in the past; that serverless applications give you the opportunity to shrink wrap your permissions. I’ve even talked about how at Protego we use automated analysis of application code and behavior to apply least privilege to each piece of your application.
The New New AppSec
The thing that crystalized for me recently is why this is so important. It’s because when you do IAM at the granularity of functions, IAM becomes an AppSec tool. It’s your AppSec people who should be driving this need, not your cloud infrastructure security people. If you can restrict the roles and policies of each function, container, bucket and table in your application to the minimum necessary, you will have prevented a large class of application attacks you worry about.
There are two reasons why this shift happened. Let me try to illustrate by re-imagining the Equifax breach.
You’ll recall that in 2017 Equifax was hacked and over 140 million people had sensitive data such as Social Security Numbers and driver’s license information stolen. Equifax was running a vulnerable version of Apache Struts on a series of VMs, and attackers were easily able to execute malicious code on these servers. From there, it was relatively easy for them to access sensitive data in the adjacent databases. In this monolithic application, the machines running Struts likely had full access to all the database tables.
Imagine, instead, that you are the Equifax team leader that just finished rebuilding the Equifax application in a cloud native fashion on Amazon Web Services, leveraging the latest technologies and architectures. This would mean you designed it for the cloud and used cloud native resources like S3, DynamoDb and Kinesis. It would also mean the large Apache Struts based application would have been re-architected as a plethora of small single-purpose functions, responding to events via API Gateway.
Ok, you’re thinking, where’s the big IAM earthquake? I’ll tell you. The first piece is that instead of five VM images housing the application, you might have 150 Lambda functions and 12 Fargate container images. Instead of creating a large risky role for each of those five VMs, you were able to craft 162 different roles, each of which can be scoped down to the bare minimum needed for that single function or container.
The second piece is that you stopped deploying your own databases, file systems and data processing pipelines, and refactored these to use cloud native resources. Cloud native resources that IAM policies can govern.
So, imagine those same attackers from 2017 came to steal SSNs from your cool new application. One big advantage is that replacing something you need to manage, like Apache Struts, with something Amazon manages, like API Gateway, means they are handling the patching that you might forget. Even more powerfully, you are now able to use IAM to be very specific about what each function can do, and that IAM is much more effective at preventing access to data you don’t need to touch in each function. Even if attacker find a way to execute malicious remote code in one of your functions, chances are IAM is going to prevent that function from accessing 140 million records in a bunch of different databases.
So, in fine-grained cloud native applications that use things like Lambda and Fargate, properly crafted IAM roles, customized for each function, will mitigate huge swaths of your attack surface, even before you get to making your developers write better code. The primary challenge we see here is getting organizations to own the shift to customized minimal roles. Technologies, like some of the capabilities of our platform, can be pivotal at enabling this change to happen in the real world where we don’t have thousands of security engineers who do review code and APIs to determine optimal policy.
Cloud native applications can be a huge leap forward in AppSec, not least because of how we now use IAM, but doing it right is both an opportunity and a challenge.