Safeguard
Application Security

Securing AWS Lambda functions

A practical guide to AWS Lambda security best practices: IAM scoping, dependency risk, secrets handling, and runtime detection for serverless apps.

Michael
Cloud Security Architect
7 min read

AWS Lambda now processes trillions of invocations a month across millions of AWS accounts, but the security model for serverless functions looks nothing like the one teams built for EC2 fleets or containers. There's no host to patch, no long-lived process for an EDR agent to watch, and no persistent filesystem to scan at rest — yet Lambda functions still ship vulnerable npm and pip packages, still assume IAM roles that can reach far more than they need, and still leak secrets through environment variables and CloudTrail logs. In April 2022, researchers at Cado Security disclosed Denonia, the first malware sample built specifically to run inside a Lambda execution environment and mine cryptocurrency using stolen AWS credentials. That single discovery changed how security teams think about serverless risk: the attack surface didn't disappear when the server did, it just moved into the code, the configuration, and the identity layer. This post walks through the AWS Lambda security best practices that actually reduce exposure, from IAM scoping to dependency management to runtime detection.

Why doesn't traditional endpoint security work on AWS Lambda?

Traditional endpoint security doesn't work on Lambda because functions run in Firecracker microVMs that are provisioned per invocation and recycled within minutes, leaving nothing for a host-based agent to install into or monitor over time. A "warm" execution environment might be reused across a handful of invocations, but AWS can reclaim it at any point, and a function's maximum execution time is capped at 900 seconds (15 minutes) regardless of what's running inside it. There's no persistent kernel to load a driver into, no long-running process tree to baseline, and no disk image sitting idle for a vulnerability scanner to mount. The closest thing to a runtime hook is the Lambda Extensions API, which AWS released in 2020 specifically so security and observability vendors would have somewhere to attach — but even that only sees the function's own execution, not the underlying host or hypervisor. That's why effective Lambda security has to shift left into the deployment package, the IAM role, and the trigger configuration, since there's no equivalent of an antivirus scan once the function is live in production.

What are the most common AWS Lambda misconfigurations that lead to compromise?

The most common misconfiguration is attaching one broad, shared IAM execution role to many functions instead of scoping a distinct, least-privilege role to each one. This is the same failure pattern behind the 2019 Capital One breach, where an attacker exploited a misconfigured web application firewall to perform an SSRF attack against the EC2 instance metadata service, retrieved temporary credentials for an IAM role with excessive S3 permissions, and exfiltrated data belonging to roughly 106 million customers. That incident predates widespread Lambda adoption, but the exact same trust model applies to Lambda execution roles: a function's role inherits whatever permissions were attached to it, and if that role can read every S3 bucket in the account because it was copy-pasted from another function, a single code injection vulnerability in that one function becomes an account-wide data exposure. A newer variant of the same problem arrived with Lambda Function URLs, launched in April 2022, which let a function be invoked directly over HTTPS without API Gateway in front of it. Function URLs support exactly two auth types — AWS_IAM or NONE — and teams that pick NONE for convenience during testing and forget to revisit it end up with an unauthenticated HTTPS endpoint sitting directly in front of application logic.

How risky are vulnerable dependencies inside Lambda deployment packages?

Vulnerable dependencies inside Lambda packages are high risk because a single compromised or outdated library bundled into a shared layer gets reused across every function that imports it, multiplying one vulnerability into dozens of exposed functions at once. Log4Shell (CVE-2021-44228), disclosed on December 10, 2021, made this concrete for serverless teams: any Lambda function or layer bundling a vulnerable Log4j 2.x version was exploitable, and because there was no shared host to patch, every affected deployment package — capped at 50 MB zipped, 250 MB unzipped, or up to 10 GB for container-image-based functions — had to be individually identified, rebuilt, and redeployed. AWS pushed emergency guidance and updated managed layers within days of disclosure, but teams without an existing software bill of materials for their Lambda estate had no fast way to even enumerate which functions were affected, let alone confirm they'd been patched. The npm ecosystem adds its own version of this risk: the ua-parser-js compromise in October 2021 pushed cryptomining and credential-stealing code to any project, including Lambda functions, that auto-installed the tainted version during a build. Without dependency scanning wired into the build pipeline that produces each deployment package, these compromises ship straight into production the next time a function is deployed.

How should Lambda environment variables and secrets be protected?

Lambda environment variables should be treated as visible, not secret, because although AWS encrypts them at rest with a KMS key, anyone holding the lambda:GetFunctionConfiguration permission can retrieve the decrypted plaintext values through the console, CLI, or API. By default that encryption uses the AWS-managed key for Lambda, which protects data at rest but does nothing to stop an over-permissioned IAM principal from reading a database password or API key straight out of a function's configuration. The better pattern is to store secrets in AWS Secrets Manager or Parameter Store and pull them at runtime using the AWS Parameters and Secrets Lambda Extension, which AWS released in 2022 specifically to let functions retrieve and locally cache secrets from an extension process instead of embedding them as plaintext environment variables that show up in configuration exports, infrastructure-as-code diffs, and CloudTrail events. This also solves the rotation problem: a secret updated in Secrets Manager takes effect on the next cache expiry without redeploying the function, whereas a secret baked into an environment variable requires a full deployment to change.

How can you detect malicious or anomalous behavior in a running Lambda function?

You detect malicious behavior in Lambda by watching for signals like unexpected outbound network calls, IAM API calls a function has never made before, or execution durations and memory usage that deviate from its normal pattern, since there's no persistent process left running to inspect after the fact. Denonia, the cryptomining malware Cado Security found in April 2022, illustrates why: it disguised its command-and-control traffic as DNS-over-HTTPS specifically to slip past network egress controls tied to VPC configurations, and it was caught because its outbound traffic and CPU usage didn't match the function's declared purpose — not because a host-based tool flagged a malicious binary. AWS's own answer to this gap is Amazon GuardDuty Lambda Protection, which launched in November 2022 and analyzes Lambda network activity logs to flag connections to known-malicious IPs, cryptomining pools, and command-and-control infrastructure without requiring any agent inside the function itself. Pairing that kind of network-layer detection with CloudTrail data events for the function's own API calls gives you the two signals — what it's talking to, and what it's doing with its IAM role — that matter most once a function is already running in production.

How Safeguard Helps

Safeguard generates a CycloneDX-format SBOM for every Lambda deployment package and layer automatically — or ingests one you already produce in CI — so you know exactly which npm, pip, and Maven packages are bundled into which function before the next Log4Shell-style disclosure forces a scramble to find out. Reachability analysis then checks whether a flagged vulnerability is actually invoked in the function's call path: if a CVE lives in a layer dependency that the handler never imports, Griffin AI deprioritizes it instead of adding another line to an already-overloaded backlog, and surfaces the smaller set of findings that are genuinely exploitable given the function's real IAM role and trigger configuration. For the vulnerabilities that are reachable, Safeguard opens an auto-fix PR with the dependency bump or the tightened IAM policy statement already drafted against the permissions the function actually uses in practice, so an engineer reviews a diff instead of writing one from scratch. Together, these capabilities shrink the gap between a CVE landing and every affected Lambda function being patched from days of manual triage to a single reviewed pull request.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.