Articles / Serverless Security: Functions, Event Injection, and Permissions
Cloud · Serverless

Serverless Security: Functions, Event Injection, and Permissions

Serverless functions — AWS Lambda, Azure Functions, Google Cloud Functions — dissolve the server you used to harden into small, event-triggered snippets of code. The operating system, the patch cycle, and the long-running process largely become the provider's problem. What remains yours are the two things that matter most for security: the permissions each function carries and the untrusted events that invoke it.

The serverless security model is genuinely different, and some old worries shrink while new ones grow. You no longer patch an OS or defend a persistent host, which removes a real class of risk. But you now run dozens or hundreds of tiny functions, each with its own execution role and its own set of triggers, and the attack surface moves from "the server" to "every function's permissions and every event that can reach it."

Event injection: input from everywhere

untrusted inputinheritsEvent sourceAPI, queue, storage, timerFunctiontreats event as trustedInjection / poisoneddependency runsExecution roleoften over-broadCloud resourcesas that roleEvent data is userinput fromunmodelled channelsBroad role →compromise pivots toeverything
Untrusted events plus an over-broad execution role make a function a pivot.

A serverless function is triggered by events, and those events arrive from many sources: HTTP requests through an API gateway, messages on a queue, object-created notifications from storage, database streams, scheduled timers. Each is an input channel, and each can carry attacker-influenced data. The mistake is treating event data as trusted because it came from "inside" the cloud. A filename in a storage event, a field in a queue message, or a header in an API event is user input — and injection flaws (SQL, command, and their cousins) apply exactly as they do in any application.

Event data is untrusted input, wherever it entersS3 event → object key: invoice-'; DROP TABLE items;--.pdf → function builds a SQL query from the key without parameterising it
Why it matters: Functions have many trigger types, so input arrives through channels a web-app threat model never considered. Every event source is an untrusted boundary — validate accordingly.

The over-permissioned function problem

Each function runs with an execution role, and that role is where serverless security most often goes wrong. Because wiring up narrow permissions is tedious, teams routinely attach broad managed policies — full access to a whole service, sometimes more. Now every function is a potential pivot: compromise its code through a dependency or an injection flaw, and the attacker inherits its role. With hundreds of functions, the odds that at least one is dangerously over-permissioned climb quickly, and that one becomes the foothold.

  • One tight role per function. Scope each execution role to exactly the actions and resources that function calls — never a shared, broad role across many.
  • Validate every event. Treat data from every trigger — API, queue, storage, stream — as untrusted; parameterise queries and validate schemas.
  • Mind the dependencies. A function is mostly third-party packages; a compromised dependency runs with the function's role. Scan and pin them.
  • Keep secrets out of environment variables. Pull them from a secret store at runtime rather than baking them into the function config.
  • Watch the function's behaviour. Log invocations and the API calls each function makes, so a function acting outside its normal pattern is visible.

The scale problem

Serverless makes deploying code so frictionless that functions proliferate faster than anyone tracks them. A team ships a function to solve one problem, wires it to an event source, grants it a broad role to save time, and moves on. Multiply that across an organisation and you have a large, fragmented attack surface with no single owner — the modern shape of shadow infrastructure.

Serverless is a real security improvement in the places it removes — no servers to patch, no hosts to harden — and a real challenge in the places it multiplies: permissions and event sources. Getting it right means right-sizing every role and distrusting every event, at a scale where functions appear faster than they are inventoried. As always, that inventory comes first: you can't defend what you haven't discovered.

Test for it — in practice
  • Pick a couple of functions and look at their execution roles — a broad managed policy means a compromise of that function's code inherits all of it.
  • Check whether a function treats its trigger data (an S3 key, a queue message field) as untrusted input, or feeds it straight into a query or shell.

Reviewing two functions is manageable; inventorying every function, its role, and its event sources at scale is what our assessment runs for you.

Keep reading
Over-Permissioned Roles and Privilege Escalation Paths