More than 25% of all publicly accessible serverless functions have access to sensitive data, as seen in internal research. The question then becomes, Are cloud serverless functions exposing your data? — which is followed by How can we assess them?
Many architectural design questions arise when it comes to the use of serverless functions in cloud environments. In this blog post, we'll examine the question of public access, focusing on the main offerings of the three leading cloud providers — AWS Lambda, Azure Functions and GCP Cloud Functions.
Already an expert? Just need a quick reference? Jump to the end of this post for a look at our cheat sheets.
Public access can be broken down into two segments:
Identity and network misconfigurations can impact the security posture of serverless functions and increase the risk of security incidents and data leaks.
Choosing the right serverless offering entails operational and security considerations. Serverless functions are often targeted by adversaries looking to reach a company’s data, and therefore can be considered a perimeter that requires appropriate security. Functions may hold a company’s intellectual property (IP) and may interact with or even alter, company data stored at other locations (e.g., S3). As such, a security gap that enables an adversary to read, write or execute functions could lead to compromised data.
Let’s look at the following scenario. You have a public website where customers can download a report of their past activity. You use a function with an HTTP trigger to fetch or compile, as well as to serve the file to the client. Should the function be public? Does the site force authentication that we might want to trickle down?
Is a function that you can reach via the public internet that requires a client certificate for interaction publicly available or does it simply lack network restrictions? What actions should we consider when it comes to public availability — invoke/execute, read, write, or something else?
For a function to be publicly accessible, it must:
From the perspective of an adversary, what can be gained from public accessibility? Alternatively, what is the risk from the defender’s perspective?
We can divide our focus into three sections based on the action type available:
While the core aspects and considerations of all serverless offerings are similar, each cloud provider’s offering has a different set of configurations. Therefore, we must look at them independently to truly understand the options.
AWS’ main serverless offering is Lambda functions. As mentioned above, we will examine two angles of public access — network and identity. First, let’s map out the different actions and permissions that might interest us and divide them into the following categories:
When made publicly available, these permissions can enable anyone to either read, write, or invoke (execute) the function. While the emphasis is usually placed on the public invocation of functions, reading the source code of a function or even changing it could be just as dangerous.
For example, an adversary who can change the source code of a function that they cannot invoke could replace the code with their own malicious code and wait for the next invocation, at which point the function would operate on their behalf.
Now that we have an idea of potentially dangerous actions, how do we determine whether they're publicly available?
The first aspect of public access is the network. Can you reach the function via the public internet? By default, the answer is yes, however, once the function is configured with access to a virtual public cloud (VPC) within your environment, the default changes. In such an environment, the function’s network accessibility depends on the various networking configurations, such as access controls on the connected VPC or configured endpoints or gateways.
The second aspect of public access is identity. Does the function require any authentication material or enforce any level of authorization? Consider the actions we discussed earlier. When the resource-based policy enables any principal to carry out an action on the function, the action can be considered public from the identity perspective. While this is true for most actions, when looking at the invocation of Lambdas through function URLs (dedicated HTTP endpoints) we must consider an additional layer of authentication.
When configuring a function URL with NONE, a resource-based policy is created that enables the principal to carry out lambda:InvokeFunctionUrl action on the function. This allows anyone to invoke the function (via the HTTP endpoint) without additional credential material.
If we specify any principal ‘*’ (everyone) when configuring a function with AWS_IAM, anyone with a valid AWS token can invoke the function (via the HTTP endpoint). Since there are no restrictions regarding who can create an AWS account, we can also consider this public.
The following screenshots are examples of invocation requests to a Lambda configured with AWS_IAM auth_type with no network restrictions. Both requests are from the same AWS entity, which is external to the Lambda’s account. During the first request, the principal in the resource-based policy is configured as a role within the Lambda’s account.
Azure Functions is Azure’s main serverless offering. We will look at two angles of public access — network and identity — as we did when analyzing AWS Lambdas. Due to the way Azure Functions code is stored, writing or reading it for each Function requires a key, and as such, it can't be considered public. For that reason, we'll focus on the invoke/execute action.
When discussing the network availability of an Azure Function, there are multiple layers that we need to consider. Networking for Functions is done at the Function app level. First, there’s the native “Public network access” configuration. Second, we must take into consideration Vnet integration (network injection) and private network endpoints.
Private endpoints and public network access are incompatible configurations (i.e., they cannot coexist). As such, if private endpoints are configured for the Function app, they cannot, by definition, be accessible via the public internet.
While Vnet integration applies to all outbound traffic (i.e., initiated by the Function) by default, it can be limited to route only private traffic through the Vnet. At that point, accessibility is determined by the Vnet’s configurations, such as the applied network security groups (NSG). However, since the integration applies only to traffic initiated by the Function, it is slightly less relevant from a public invocation perspective.
We have multiple options for the identity side of Azure Functions invocation. At the Function app level, we can enforce authentication using Entra ID or a variety of OAuth and OIDC-based identity providers (IdPs). At the individual Function level, we can enforce key-based authentication or no authentication at all (anonymous).
A publicly invocable Function (from the identity aspect) scenario occurs only when the Function app either has no authentication configured, or the “Allow unauthenticated access” option is enabled — and the individual Function’s authorization level is set to “Anonymous.”
Setting the individual Function as anonymous is carried out during its creation.
We can also view the postcreation configuration in the code.
GCP Cloud Functions is GCP’s main serverless offering. Just like we analyzed the previous two offerings, here we will examine network and identity to define public access. We’ll look at the invocation in GCP Cloud Functions as we did with Azure Functions.
GCP offers two generations of Cloud Functions — Gen 1 and Gen 2. Gen 2 is built on top of Cloud Run and adds efficiency and support for further events. As such, it benefits from Cloud Run’s inherent security features, such as sandboxing and the Function’s containerization. In addition, each generation requires different permissions.
GCP enables three default configuration options for networking: allow all traffic, allow internal traffic only, allow internal traffic and traffic from cloud load balancing. Among the three, only allow all traffic permits connections from the public internet.
Similar to Azure, in GCP Cloud Functions we can connect our Function to a VPC for outbound connections, which is the point where the VPC security mechanisms, such as firewall rules, determine the permitted connections. The VPC connection, though, applies only to traffic initiated by the Function, making this configuration less relevant in the public invocation discussion.
We must address multiple considerations regarding the identity end of GCP Cloud Functions. The first is the native “Allow unauthenticated invocation” configuration, which permits public access from the identity aspect while the alternative requires authentication.
But it does not end here. If we look into the configurations, we’ll notice that we can add roles to different principals. GCP provides us with two special identifiers:
Since anyone can create a personal Google account, we can treat the two identifiers as “Everyone.” By adding the permissions mentioned above (or more commonly, the Cloud Functions Invoker or Cloud Run Invoker predefined roles that contain the permissions) to either the allUsers or allAuthenticatedUsers principals, we enable public access from the identity aspect.
We can enable unauthenticated access to invoke the function during its creation.
Alternatively, we can require authentication and add permissions to the allUsers group (in this case a Gen two instance).
Once we apply the configuration, we can see that GCP changes the authentication status to “Allow unauthenticated.”
Before:
After:
Given the above configuration and assuming no network restrictions, the Function is publicly invocable.
While serverless computing often provides a simpler, more efficient, more scalable, and less expensive solution to build applications, it is subject to many potential pitfalls, regardless of your vendor of choice. In this blog, we briefly covered some configurations of the main cloud providers’ key serverless offerings, and showed how easily they can become convoluted and lead to errors.
In the best case scenario, you miss one configuration while another keeps you safe (e.g., misconfigured network access but successfully configuring identity controls). In the worst case scenario, your function is publicly accessible and, depending on context, could act as a pivot for an adversary within your environment. As such, it is important to understand the services and each of their configurations, as well as to set up boundaries, good practices and security measures to prevent these misconfigurations from happening.
For a fuller understanding of how your data is exposed in the cloud, read our comprehensive State of Cloud Data Security 2023 report, which not only sheds light on critical aspects of cloud data security but also provides actionable steps to defend your valuable data.
By submitting this form, you agree to our Terms of Use and acknowledge our Privacy Statement. Please look for a confirmation email from us. If you don't receive it in the next 10 minutes, please check your spam folder.