Performance-Optimized Runtime Protection for Serverless Functions
In order to protect serverless functions while they are running, you need to consider the very short execution times and ensure that you’re not slowing down applications or increasing cloud usage costs. In Aqua CSP 4.2, we’ve now introduced advanced runtime protection that does just that, while complementing our existing risk assessment, function scanning, and function assurance capabilities.
Reducing Risk in Serverless Functions
As I’ve written in previous blogs, serverless functions (e.g., AWS Lambda) can introduce risks through vulnerabilities in the function code, sensitive data such as cloud account keys embedded as plain text in the function code or environment variables, or the most common mistake of over-provisioned permissions and roles that allow access to multiple resources. Many of these risks can be mitigated in the CI/CD pipeline, before the function is launched. You can find the optimal least privilege model for each function to improve its security posture while not impacting its functionality, preventing developers from being overly liberal with granting permissions and capabilities.
Despite the preliminary precautionary steps, there are still some risks that materialize during the function’s runtime and require advanced security controls to mitigate them. Serverless function execution durations are usually extremely short. The maximum allowed on AWS Lambda is 15 minutes, but that limit is rarely reached. More often, it’s fractions of a second or only a few seconds. That’s great for security, as the lack of persistence challenges classic attack scenarios. However, there are new attack options that can exploit functions (for example, mining for cryptocurrency) or can use the function as a jumping board to access additional resources in the cloud account. Those attacks typically use code injection techniques or serialization attacks that aren’t preventable during the development and deployment stages.
To solve these types of attacks, we need more proactive real-time controls that protect the function’s runtime.
Aqua Runtime Protection for Serverless
If you burden a function with resource-intensive security controls, such as validation of all inputs and outputs, monitoring of all processes, logging all events, encrypting all sensitive data, you will probably add intolerable lags to function execution time and consequently sharply increase function costs, cause poor performance (delay), significantly increase timeouts duration (a security risk by itself), and cause unnecessary friction with development teams. This approach would take too high of a toll and negate many of the benefits that drive people to use serverless in the first place.
After a thorough analysis of the different risks and their potential mitigations, we decided to make performance optimization and frictionless security our top priority when designing our runtime protection, allowing us to stop attacks while keeping the impact on function invocation time below 3ms (and using less than 2Mb of memory). To mitigate runtime risks, we developed deterministic measures to prevent runtime attacks with deterministic measures to identify undeniable attacks, with no false positive alerts nor any added risk to user environments.
Introducing Aqua NanoEnforcer
The latest member of the Enforcers family is Aqua NanoEnforcer. This is completely rewritten code designed specifically for serverless functions with dedicated controls and optimal performance. The NanoEnforcer can be added to any AWS Lambda function (Azure and GCP support planned later this year) by adding it as Lambda with no modifications to the function code or its runtime. Defining the Aqua NanoEnforcer in an Aqua server creates a ZIP file that can then be uploaded as a Lambda Layer within your AWS account. That Layer is associated with the functions that users want to protect.
To simplify the deployment in large scale environments, we also created a new tool for the creation of the NanoEnforcer layer that adds it automatically to the desired functions.
Runtime Security Policy and Controls
Aqua runtime protection for serverless provides some unique controls. There’s a resemblance to some of our container runtime controls, but we favored simplicity and effectiveness in order to keep the performance impact low.
There are three key controls:
- Preventing malicious executables in functions: This blocks malicious code injection (“child processes”) from being added to a running function, basically whitelisting what the function was invoked with and blocking any attempt to run new code. We protect the function’s “/tmp” directory against unauthorized abuse, since this is the only location which attackers can write to.
- Blacklisting of forbidden executables: Allows security teams to control the types of executables that developers are allowed to include in functions, preventing the use of cryptocurrency mining or malware, for example.
- Honeypots: This is a novel control that detects malicious intent by luring attackers to exploit what is perceived to be “low hanging fruit” embedded into the function code or its environment variables. It’s unique in that it provides deterministic identification of function abuse and a clear indication of a successful breach.
Testing the Aqua Serverless Runtime Protection
You can test the Aqua Serverless Runtime Protection with OWASP’s ServerlessGoat.
ServerlessGoat is a deliberately insecure, realistic AWS Lambda serverless application, maintained by OWASP.
Here is an example of an attempt to inject unvetted code into a function without the Aqua NanoEnforcer Layer.
Note how sensitive information, such as AWS credentials (secret key, access key, session token) is stored as plain text environment variables and could be exposed.
The Aqua NanoEnforcer blocks the code injection attempt, preventing the exposure of the AWS credentials which were stored in plain text:
Summing it up
With Aqua CSP 4.2, we’ve come full circle and now offer full lifecycle security for both containers and serverless. We didn’t simply take our existing container runtime security controls and label them as “serverless”. This is an entirely new architecture which uses the Aqua NanoEnforcer. It provides performance-optimized and scalable protection, as well as unique features tailored to the specific cloud serverless platform (AWS Lambda currently, with more to come).