Best Practices for Secure Serverless Development.
Adoption of Serverless
Did you know that as of late 2019, four out of 10 enterprises have already adopted serverless technologies? That’s considerable growth considering that the current serverless landscape was only introduced five years ago (at 2014’s AWS re:Invent). Since then, multiple cloud providers have introduced their own serverless platforms, and the market is expected to hit $7.72 billion by next year.
What’s driving the adoption of serverless? As we’ve discussed before, serverless is attractive for many reasons. For example, serverless is:
- Cost-Effective – with serverless, you pay for consumption, not capacity.
- Immensely Scalable – cloud providers automatically start parallel executions when there is more demand and can scale those down when there is less.
- Faster – it enables developers to write code and then execute it on-demand without worrying about infrastructure provisioning, so companies can take concepts from idea to launch much more rapidly than with traditional physical servers.
Mitigating Security Risks
While serverless has many benefits, it also has some drawbacks, especially when it comes to security. For example, the very nature of serverless (e.g. distributed across many servers) gives a malicious actor more points of entry to attack. Additionally, delivering security for various serverless functions can be challenging. Function components, over-provisioning and data validation can all be points of vulnerability for serverless development.
With more organizations opting in to serverless, it’s important to avoid these and other potential security pitfalls. That’s why we’ve put together our top four best practices for secure serverless development:
1. Lambda Functions should not call other Lambda Functions directly
Use the AWS API Gateway as a security buffer to your deployed cloud functions. By default, an API Gateway route that communicates with your deployed cloud function does not prevent requests without authorization. If there are functions that require authorization, let the API Gateway handle that responsibility. An API Gateway has the ability to prevent unauthorized access by managing authentication and authorization, thereby rejecting all invalid requests.
2. Lambda Functions should not call other Lambda Functions directly
Functions calling other functions is counterproductive. For example, while it can be enticing to use a Lambda function to abstract the business logic processing of another Lambda function, it actually causes a lot of problems:
a. It’s difficult to debug one Lambda function. When functions call other functions you increase the amount of debugging you have to do, giving developers a headache and increasing your attack surface because you’re now required to handle the security elements of multiple Lambda functions.
b. It increases costs because you’re charged 2x more than before for each function invocation.
3. Avoid using connection-based services, and lock down your IAM roles
Serverless works best with services rather than connections.
Connection based services often require a significant amount of processing. These systems, like RDBMS for example, query data from multiple locations, and the results must be assembled for presentation. Additionally, connection-based services might require other transaction frameworks that add significant overhead to the write process and further slow down speed and scalability.
When taking advantage of this approach, do not forget to lock down your IAM roles for interacting with these services. It can be exciting to get started storing and processing data with DynamoDB in half the time your used to setting up a NoSQL database, like MongoDB, but don’t forget to ensure that the Lambda function has the least amount of privilege when interacting with that service. Skipping this step only increases your attack surface for a malicious character to start manipulating your DynamoDB infrastructure because its IAM role allows it to.
4. Decouple business logic from the handler
The “handler” is the root of Lambda functions. It’s the method in a Lambda function that processes events and it provides parameter values that are passed to the endpoint of the event. It’s best to take those values and pass them to another function that handles the business logic – essentially decoupling the code, making it more isolated and, in turn, easier to test for bugs and vulnerabilities. Decoupling in this way also allows you to reuse business logic throughout your app.
Let’s build something beautiful together.
Software Developer, Continuous Learner, & Hockey Addict