Episode 56 — Secure serverless architectures by understanding their real attack surfaces
In this episode, we make serverless security feel concrete by understanding the real attack surfaces, because hardening fails when you start from the wrong mental model. Serverless platforms are often described as no servers to manage, which is true operationally, but it can accidentally encourage the idea that there are fewer security responsibilities. In reality, serverless shifts the risk away from patching operating systems and toward identity, triggers, permissions, and data movement. If a function can be invoked, if it can read secrets, or if it can reach sensitive data stores, that is the attack surface, even if the runtime itself is managed by the provider. The goal here is to build the habit of thinking like an attacker and like a defender at the same time: how could this function be triggered, what authority would it run with, and what could it touch. When you answer those questions clearly, hardening becomes straightforward and repeatable. If you skip them, you end up hardening the wrong thing and leaving the real pathways open.
Before we continue, a quick note: this audio course is a companion to our course companion books. The first book is about the exam and provides detailed information on how to pass it best. The second book is a Kindle-only eBook that contains 1,000 flashcards that can be used on your mobile device or Kindle. Check them both out at Cyber Author dot me, in the Bare Metal Study Guides Series.
Serverless, in this context, is event-driven functions with managed runtime boundaries, where the platform handles scaling, execution infrastructure, and much of the runtime isolation. A function is code that runs in response to events, such as an HTTP request, a message on a queue, a file upload, a timer, or a change in a data store. Managed runtime boundaries mean you do not manage the underlying host, and you often have limited visibility into the execution environment beyond logs and metrics. The platform manages how functions are scheduled and isolated, and it provides integration features that make it easy to connect event sources to code. That integration is powerful, but it also means that the path into your code is often defined by configuration, not by a hand-built application front end. In serverless, configuration choices are security choices because they determine what can invoke the function and under what context. The function may be small, but its impact can be large if it is connected to high-value data or privileged services. Serverless is therefore not a reduced security problem. It is a different security problem where the control plane and identity plane matter more than the host.
Serverless shifts risk to identity, triggers, and permissions because those elements determine both reachability and authority. In traditional architectures, an attacker often has to find a network path into a server, exploit a vulnerability, and then escalate privileges. In serverless, the attacker may not need a host exploit at all. They may simply trigger a function through an exposed event source, or abuse a configuration that allows invocation by unintended callers. If the function runs with a powerful identity and broad permissions, the attacker can get high-impact outcomes by driving the function to do work on their behalf. Triggers become the entry points, because they decide what requests and events can reach code. Identity becomes the execution context, because it decides what the code is allowed to do once it runs. Permissions become the blast radius, because they define which data and services can be accessed, modified, or exfiltrated. This is why serverless hardening starts with the configuration graph, not with the runtime. The runtime is managed, but the invocation and authorization model is yours to configure responsibly.
A realistic scenario is a function trigger being abused to run code that performs unintended actions. Imagine a function that processes uploaded files and writes results into a storage bucket. The trigger is configured so that any upload to a certain location invokes the function, and the function trusts the event payload and processes the object. An attacker discovers that they can upload objects to that location because the bucket has broad write permissions or because the upload interface is exposed publicly. By uploading a crafted object or repeatedly uploading many objects, the attacker can cause the function to execute at scale. They may use this to force expensive compute usage, to exploit weak parsing logic, or to cause the function to access internal resources if the function reads secrets or connects to internal services. Even if the function code has no obvious vulnerability, the attacker can still abuse it as a capability by controlling when it runs and what inputs it receives. The attacker’s goal is not always remote code execution in the classic sense. It can be driving the function to do authorized work with unauthorized intent, such as writing data, calling an internal API, or generating logs that leak sensitive content. The risk begins with the trigger and the event trust model, not just with the code.
Two pitfalls make these scenarios common: broad permissions and untrusted events treated as safe inputs. Broad permissions happen when a function identity is granted wide access to storage, databases, secrets, or control-plane operations because it was easier than scoping it precisely. In serverless, broad permissions are especially dangerous because the function can be invoked in ways developers did not anticipate, and the platform will happily execute it at scale. Untrusted events as inputs happen when a function assumes that the event source is trustworthy or that the payload is well formed, and it processes it without validation, limits, or sanity checks. Many serverless functions begin as internal automation and later become connected to more exposed triggers as the architecture evolves. When that happens, the function inherits a new threat environment without the code being updated to handle it. Another pitfall is forgetting that triggers can be chained, so an attacker may not need direct invocation if they can influence an upstream event source. The result is that a function designed for controlled internal events becomes reachable through a path nobody modeled. To avoid these pitfalls, you treat triggers as public interfaces unless you can prove they are constrained, and you treat function permissions as high-impact because they often bridge into sensitive resources.
Quick wins start with inventorying functions, triggers, and permissions, because you cannot secure what you cannot see. An inventory means you can answer basic questions: what functions exist, what events can invoke them, and what identities do they run as. It also means you know which functions are internet-facing through HTTP triggers or through exposed event sources, and which functions are internal only. The inventory should include permissions at a meaningful level, such as what storage buckets can be read, what databases can be queried, what secrets can be accessed, and what network paths are available. Inventory also includes understanding concurrency and scaling characteristics, because a function that can run thousands of times per minute represents a different risk than a function that runs a few times per hour. The practical benefit is that inventory creates a map of attack surface and blast radius, which allows you to prioritize the highest risk functions first. Without inventory, security becomes anecdotal, driven by whichever function the team happens to be discussing. With inventory, you can focus on the functions that are both reachable and privileged, which is where incidents tend to emerge.
A useful practice is mapping an event source to what the function can touch, because that mapping reveals risk more clearly than any single configuration file. Start with the trigger and ask how an attacker could influence it. If the trigger is an HTTP endpoint, the influence path may be obvious. If the trigger is a queue or a storage event, the influence path may be through write permissions to that queue or bucket, or through an upstream system that produces events. Then map what happens when the function runs: which secrets it loads, which data stores it reads, which services it calls, and what it can write or delete. The risk emerges from the combination, not from either side alone. A publicly reachable trigger paired with a function that can read sensitive data is high risk. A restricted trigger paired with a function that has broad control-plane permissions is also high risk because a single upstream compromise could yield outsized impact. The mapping should also consider output destinations, because a function that writes to storage or sends messages can be abused to stage or move data. When you can draw this mapping in words, you can explain the function’s risk posture quickly and accurately.
Secrets, environment variables, and logs are common exposure points in serverless because they are convenient mechanisms that can leak sensitive context if mishandled. Secrets are often fetched at runtime or injected into environment variables, and the function may inadvertently log them, echo them in errors, or expose them through debugging behavior. Environment variables can contain configuration that reveals internal endpoints, identifiers, or credentials, and they are sometimes accessible through management interfaces or inadvertently included in diagnostic dumps. Logs are especially sensitive because serverless functions can log event payloads, headers, and error traces, and those logs can become a secondary data store containing sensitive information. If logs are accessible to broad audiences or retained without protection, they become a quiet leak vector. Attackers may also use functions to intentionally generate logs that contain sensitive data, turning logging into an exfiltration pathway if they can invoke the function with crafted inputs. This is why hardening includes deciding what should never be logged, ensuring logs are protected, and ensuring secret access is scoped tightly. Secrets and logs are part of the attack surface because they represent high-value data flows that often bypass normal application data protections.
Limiting outbound access is an important way to reduce data exfiltration potential, because many serverless compromises end with data leaving controlled boundaries. If a function can call arbitrary external endpoints, an attacker who can influence the function’s behavior can potentially use it to send data out. Outbound limitations can be implemented by constraining network routes, restricting which destinations are allowed, and ensuring that functions that do not need internet access cannot reach the internet. Even partial constraints can help by forcing data movement to go through controlled channels rather than arbitrary destinations. Outbound restrictions also reduce the value of compromised secrets, because a function that cannot reach external endpoints has fewer ways to leak those secrets. Another benefit is reducing command-and-control possibilities, where attackers use outbound connectivity to coordinate actions or download additional payloads. Outbound limitation must be balanced with operational needs because many functions legitimately call external APIs, but the key is to scope outbound access to what is required rather than leaving it open by default. When outbound access is constrained, an attacker’s options narrow, and detection becomes easier because unexpected outbound attempts become high-signal events.
Logging and monitoring for serverless must capture both function behavior and trigger behavior because the entry point and the execution are equally important. Function logs should capture execution outcomes, errors, and key operational signals without leaking sensitive payloads. Trigger logs should capture invocation details, such as who invoked the function, from where, and with what frequency and volume. Monitoring should look for anomalies like spikes in invocations, unusual event sources, repeated failures followed by success, and unusual access patterns to downstream services from the function identity. It should also monitor configuration changes, such as changes to triggers, changes to permissions, and changes to environment variables or secret bindings. Serverless environments change often, and small changes can create new exposure quickly, so configuration change visibility is crucial. Alerts should focus on high-signal indicators, like a function gaining broad permissions, a trigger becoming publicly reachable, or a sudden surge in invocations that suggests abuse. Monitoring becomes more effective when it can correlate invocation events with downstream data access events, allowing you to see not only that the function ran, but what it did. Visibility is what lets you detect abuse early and respond confidently.
The memory anchor for serverless hardening is trigger, identity, data access, and visibility. Trigger is how the code can be reached, and whether that reachability is constrained or broadly exposed. Identity is the execution context and what permissions the function has when it runs. Data access is what the function can read, write, delete, or transmit, including secrets and sensitive datasets. Visibility is what you can observe about invocations, configuration changes, and downstream actions, which determines how quickly you can detect and investigate misuse. This anchor works because most serverless incidents are driven by a mismatch in one of these areas: a trigger becomes more exposed than expected, an identity becomes more privileged than needed, data access becomes broader than intended, or visibility is insufficient to detect abnormal use. If you can answer these four dimensions for every function, you can usually identify the highest risk functions quickly. The anchor is also repeatable across providers because every serverless platform has triggers, identities, permissions, and logging. It keeps your focus on the real attack surface rather than on the managed runtime itself.
A spoken checklist for serverless risk drivers helps teams stay consistent, especially during reviews. You start by stating the triggers and whether they are constrained to trusted sources. You then state the function identity and list the most powerful permissions it has, especially access to secrets, storage, databases, and control-plane actions. You then state the data paths, including what the function reads and where it writes, and whether any of those paths cross sensitive boundaries. You then state how outbound access is constrained and whether the function can reach the internet or only specific destinations. You then state what is logged, what is deliberately not logged, and how logs are protected and retained. You also state what monitoring exists for invocation anomalies and configuration changes. Finally, you confirm that the function’s configuration is reviewed after updates and that ownership is clear. This checklist is short enough to repeat in meetings, but it captures the major drivers of serverless risk. The purpose is to create a consistent evaluation habit so serverless security does not depend on who happens to be reviewing.
A quick assessment sequence for any newly deployed function should be designed for speed, because new functions appear frequently and security review cannot be a bottleneck. The first step is to identify the trigger and confirm whether it is intended to be reachable externally or only internally. The second step is to inspect the function identity permissions and ensure least privilege for the function’s stated purpose, with particular attention to secrets and sensitive data stores. The third step is to evaluate input trust, confirming that the function validates event structure and size and does not assume event sources are safe without constraints. The fourth step is to check exposure points like environment variables and logging, ensuring secrets are not exposed and logs do not capture sensitive payloads. The fifth step is to verify outbound access constraints, especially for functions that handle sensitive data, so exfiltration paths are limited. The sixth step is to confirm monitoring and alerts for invocation spikes, failed authentication patterns, and configuration changes. This sequence is not about deep code review. It is about configuration and permission posture, which are the most common early failure points. When this sequence is routine, serverless deployments remain fast while avoiding predictable risk.
To conclude, pick one function in your environment and list its triggers and permissions in a way that reveals its true attack surface. Name the triggers and describe who can cause them to fire, including any upstream systems or permissions that influence the event source. Then describe the function identity and the most important permissions it holds, especially access to secrets, storage, databases, messaging systems, and any control-plane capabilities. Note what the function can write or delete, because integrity and availability risks matter as much as confidentiality. Finally, state what logging and monitoring exist for that function, including whether you can detect invocation spikes and whether configuration changes are visible. If you cannot list triggers and permissions clearly, treat that as a signal that inventory and mapping work is required before you trust the function’s security posture. The decision rule is simple: if you cannot describe how the function is triggered and what it can touch, you cannot meaningfully harden it, so make that mapping explicit before you assume it is safe.