Threat Modeling and Attack Surface Analysis Questions
Systematically identifying how a system can be attacked and where its exposure lies. Covers structured methodologies (STRIDE, PASTA, DREAD, OCTAVE, attack trees), enumerating and reducing attack surface, mapping trust boundaries and data flows via DFDs, profiling likely threat actors, and prioritizing identified threats by likelihood and impact during design. Includes applying this methodology to specific architectural substrates (cloud-native and serverless, microservices, ML/AI systems, IoT, CI/CD pipelines, cryptographic subsystems) and operationalizing it as a recurring program (SDLC integration, governance, tooling, KPIs). The proactive 'think like an attacker before you build' discipline: distinct from live penetration testing (the adversarial validation of a built system), from runtime detection/monitoring (recognizing an attack already in progress), and from implementing the resulting security controls (a separate design-and-build discipline).
Design a threat model for a multi-tenant serverless (FaaS) platform that allows customers to deploy code and stores tenant data in shared cloud resources. Identify unique serverless and multi-tenant threats (for example: environment variable leakage, ephemeral-sandbox escapes, cross-tenant data access), propose mitigations (sandboxing, per-tenant encryption, IAM design), and describe how you would verify tenant isolation at scale.
Sample Answer
Direct answer
A multi-tenant Function-as-a-Service (FaaS) platform's core threat is that the platform itself, not any individual tenant's code, is the trust boundary customers are relying on: a bug in how the platform isolates one tenant's execution, storage, or configuration from another's turns a single tenant's compromise (or even just a single tenant's normal, non-malicious code) into every other tenant's incident. The threat model has to focus on three specific serverless-shaped failure modes, environment-variable and configuration leakage across the shared control plane, sandbox-escape or side-channel leakage between co-located function executions, and cross-tenant data access through IAM (Identity and Access Management) or storage mistakes, and the mitigations (strong sandboxing, per-tenant encryption keys, and IAM designed so tenant identity is enforced structurally rather than by convention) have to be verified continuously, not assumed to hold just because they were designed correctly once.
Structured elaboration
Unique serverless and multi-tenant threats
- Environment-variable and configuration leakage: FaaS platforms commonly inject per-function configuration (API keys, connection strings, feature flags) as environment variables at invocation time. A platform bug, a misconfigured logging pipeline that captures the environment on error, or a debugging feature left enabled can expose one tenant's environment variables to another tenant's function, or to platform operators without proper access controls, turning a convenience mechanism into a cross-tenant secrets leak.
- Ephemeral-sandbox escape: each function invocation runs in a short-lived, supposedly isolated execution environment (a container, a micro-VM, or a language-level sandbox depending on the platform's architecture). An escape from that sandbox, or a side-channel that leaks information across co-located executions sharing the same underlying host (timing side-channels, shared-cache side-channels, or simply a bug in the isolation boundary itself), lets one tenant's function observe or affect another tenant's execution despite the platform's isolation promise. The "ephemeral" property helps (a compromised sandbox is destroyed after the invocation, limiting persistence) but does not eliminate this class, since the damage from even one successful cross-tenant read can be done before the sandbox is torn down.
- Cross-tenant data access: the most common real-world instance is not an exotic sandbox escape but a mundane IAM or storage-layer mistake, a shared database table with tenant ID as just another column rather than as an enforced partition boundary, a storage bucket path convention relied on for isolation with no actual access-control enforcement behind it, or an over-broad IAM role shared across functions from different tenants because provisioning per-tenant roles felt like unnecessary overhead. This is the highest-likelihood threat in the list precisely because it does not require defeating the platform's sandboxing at all; it only requires a mistake in how tenant identity is enforced at the data layer.
- Cold-start and warm-container reuse leakage: a subtler variant of sandbox escape specific to FaaS performance optimizations, where a platform reuses a "warm" execution environment across invocations (including, in a platform-level bug, across different tenants' invocations) to avoid cold-start latency; residual state (memory, temp files, cached credentials) from a prior invocation persisting into a reused environment is a realistic leakage path that would not exist in a platform without this optimization, worth calling out separately because it is specific to how FaaS platforms actually achieve their performance characteristics.
- Function event injection: since the FaaS platform, not the tenant, controls how invocation events are constructed and routed, a flaw in event routing or a shared event bus without strict per-tenant partitioning could let a crafted or misrouted event trigger another tenant's function with attacker-controlled input, blurring the line between "cross-tenant threat" and a more conventional injection threat.
Mitigations
- Sandboxing: use a hardware-virtualization-backed isolation technology (a lightweight virtual machine per function invocation, rather than relying solely on OS-level container namespacing) for the execution boundary, since VM-level isolation has a meaningfully smaller shared-kernel attack surface than container-only isolation. This directly targets the sandbox-escape threat above; it does not by itself address the more common data-layer cross-tenant threat, which needs its own mitigation.
- Per-tenant encryption: encrypt each tenant's data at rest with a distinct, tenant-scoped encryption key (rather than one platform-wide key), so that even if a storage-layer isolation bug allows one tenant's code to read bytes belonging to another tenant, those bytes are not usable without also compromising that specific tenant's key. This converts a data-layer access-control failure from a full breach into, at worst, an encrypted-and-unreadable exposure, a meaningfully smaller incident.
- IAM design that enforces tenant identity structurally: derive each function invocation's permissions from the tenant context automatically (a per-tenant IAM role or policy scoped to only that tenant's resources, attached based on which tenant owns the function, not manually assigned and hoped to stay correct), rather than relying on application code to remember to filter by tenant ID on every query. The goal is that a missing
WHERE tenant_id = ?clause in one function's code cannot leak cross-tenant data, because the underlying IAM policy would deny the cross-tenant access attempt regardless of what the application code does or fails to do. - Environment-variable isolation: scope configuration injection so a function can only ever receive its own tenant's environment variables, enforced at the platform's configuration-provisioning layer rather than by convention, and exclude environment contents from default logging/error-capture pipelines (or redact known-secret-shaped values) to close the accidental-logging leakage path.
Verifying tenant isolation at scale
A design that is correct on paper still needs continuous, automated proof it holds in the live system, because the failure mode (a config change, a new feature, a platform update) that breaks isolation rarely announces itself:
- Automated cross-tenant access test suite: a synthetic "canary" tenant whose functions periodically and automatically attempt to read or write data belonging to a second synthetic tenant, expecting every attempt to fail; run continuously in production (using dedicated non-customer test tenants, not real customer data) rather than only at release time, since isolation-breaking changes can land at any point, not just at major releases.
- Chaos-style isolation drills: deliberately induce the platform's warm-container-reuse path and verify no residual state from a prior tenant's invocation is observable in the next one; deliberately attempt IAM policy boundary violations from a low-privilege test identity and confirm they are denied, rather than assuming the policy is correct because it was reviewed once.
- Periodic independent penetration testing scoped specifically to cross-tenant boundaries, distinct from general application security testing, since the properties being verified (can tenant A ever observe or affect tenant B) are specific to the multi-tenant architecture and not something a generic security scan is designed to probe for.
- Formal or structured review of the IAM policy generation logic itself (not just spot-checking individual policies), since if per-tenant policies are generated programmatically from a template, a single bug in the template generator can silently create the same isolation gap across every tenant simultaneously, which is a much larger blast radius than a single misconfigured policy and correspondingly deserves more rigorous verification of the generator, not just its output.
Worked example
A concrete trace of the highest-likelihood threat (cross-tenant data access via an IAM/storage mistake, not an exotic sandbox escape) to make the mitigation's effect visible rather than asserted:
- A platform engineer adds a new feature: functions can write structured logs to a shared analytics data store for cross-tenant platform-health dashboards. The implementation uses one IAM role, shared across all tenant function executions, with write access to the entire analytics store, because provisioning a distinct scoped role per tenant felt like unnecessary setup for what seemed like a low-risk logging feature.
- Without per-tenant IAM enforcement: a bug (or a deliberately crafted input) in one tenant's function causes it to write, or in a worse case read, records outside its own tenant's partition of the analytics store, because the underlying IAM role technically permits it and nothing else in the path enforces the boundary. The application code's tenant-scoping logic, if it exists at all, is the only thing standing between this and a cross-tenant data exposure.
- With the mitigations above: the per-tenant IAM design means the shared analytics-write role would never have been provisioned that broadly in the first place; each tenant's functions would carry a policy scoped to write only to that tenant's own partition, so the same application-code bug produces an access-denied error instead of a cross-tenant write. The canary suite is the weaker half of this defence and worth being honest about: it only probes the resources it has been pointed at, and the analytics store in step 1 is brand new, so a canary that has been green for months says nothing about it until someone extends the suite to cover it. That extension is the step that gets skipped, which is why adding a cross-tenant canary case should be a required part of shipping any new shared data store rather than a follow-up task, and why canary greenness is only evidence for the surface the canary actually reaches.
- This illustrates why the direct answer frames data-layer IAM mistakes as the highest-likelihood threat: nothing here required defeating the platform's execution sandboxing at all, it required exactly one convenience shortcut in a role definition, which is a far more common real-world failure than a genuine sandbox escape.
Trade-offs and pitfalls
- Treating sandbox-escape defenses as sufficient is a common blind spot. VM-level isolation is genuinely valuable against the sandbox-escape and side-channel threats, but the worked example above shows the more likely real-world incident bypasses the sandbox boundary entirely via a data-layer mistake; a threat model that spends its mitigation budget disproportionately on exotic escape scenarios while under-investing in IAM and storage-layer tenant enforcement is optimizing for the less likely path.
- Per-tenant encryption keys add real operational complexity (key management, rotation, and the availability implications of a key being unavailable) that scales with tenant count; at very large tenant counts this needs a managed key-service design, not ad-hoc per-tenant key handling, or the mitigation itself becomes an operational risk.
- "Convention-based" tenant scoping in application code is a recurring wrong turn. Relying on every engineer, on every function, to remember to filter by tenant ID correctly does not scale and does not survive refactoring; the IAM-enforces-it-structurally approach exists specifically because human-remembered conventions are where this class of bug actually originates in practice.
- Isolation testing that only runs at release time misses the majority of the real risk window. Most isolation-breaking changes are small, incremental configuration or code changes that land continuously, not only at major releases; continuous automated canary testing in production is what actually closes this gap, and a periodic-only testing cadence gives a false sense that isolation is being actively verified when most of the calendar is actually uncovered.
How does threat modeling change for serverless and cloud-native architectures compared to traditional VM-based designs? Identify unique attack surfaces (e.g., functions, event sources, IAM roles, managed services), and list recommended mitigation patterns and observability practices.
Sample Answer
Direct answer
Threat modeling a serverless or cloud-native system does not change the methodology, STRIDE and trust-boundary mapping still apply, but it changes what counts as an asset and where the boundaries actually sit. A traditional virtual machine (VM)-based design has a small number of coarse-grained trust boundaries (network perimeter, host operating system, application process); a serverless design has many more, finer-grained boundaries, because every function invocation, every event source, and every managed service call is itself a boundary crossing with its own identity and permission set. The four attack surfaces that specifically change are functions (short-lived, individually invokable units of compute), event sources (the triggers that invoke a function, each an entry point an attacker can target), Identity and Access Management (IAM) roles (the permission model, since there is no host to compromise, the permission boundary becomes the primary target), and managed services (databases, queues, storage, each with its own exposed configuration surface instead of being hidden behind an application server you control).
Structured elaboration
Why the boundaries move, not just multiply
In a VM-based design, an attacker who wants to reach a database typically has to compromise the network perimeter, then the host, then the application process, then use the application's own database credentials, a small number of sequential hops. In a serverless design, many of those hops are replaced by direct service-to-service calls authorized purely by IAM policy: a function invoked by an event has no host to compromise at all, so the permission boundary (does this function's role actually need to read this table) becomes the primary line of defense instead of one layer among several. This is the single biggest mental shift: the attack surface moves from "can I get code running here" to "what can already-authorized code reach."
The four attack surfaces, in detail
- Functions: individually invokable, ephemeral units of compute. Because each function typically has its own IAM role, an over-permissioned function (one granted broader access than its actual job needs) is a standing risk even if it is never directly compromised, since any bug in it (for example, unsanitized input passed to a downstream call) inherits the full blast radius of its role.
- Event sources: the triggers that invoke a function (an HTTP API gateway, a message queue, a storage-upload notification, a scheduled timer). Each is a distinct entry point with its own authentication model; a storage-upload trigger, for instance, fires on any object landing in a bucket, so if the bucket accepts uploads from untrusted parties, the function is effectively processing attacker-controlled input by design, not by accident.
- IAM roles: the permission model attached to each function and resource. Because there is no host-level compromise step to slow an attacker down, an overly broad role (wildcard permissions, or permissions scoped to a whole resource type rather than a specific resource) is directly exploitable the moment the function it's attached to has any other weakness.
- Managed services: databases, queues, object storage, and similar services that used to sit behind an application server you fully controlled now expose their own configuration surface directly (bucket policies, queue access policies, database network rules). A misconfiguration here is not hidden behind an application layer; it is the entire perimeter for that resource.
Trust boundaries: what changes when a boundary crosses from on-premises to a public cloud tenant
A useful way to see the shift is to threat-model the same component before and after a move from an on-premises data center to a public cloud tenant, since that move crosses several trust boundaries that used to not exist:
- Spoofing: on-premises, identity is often network-location-based (trusted because it's on the internal network); in a cloud tenant, identity must be explicit (IAM role, service identity, signed request) because network location no longer implies trust.
- Tampering: on-premises, data in transit between internal hosts is sometimes unencrypted under an assumption of a trusted network; crossing into a shared-tenancy cloud environment removes that assumption, so encryption in transit between services becomes mandatory rather than optional.
- Repudiation: on-premises logging is often host-based and can be tampered with by anyone who compromises the host; cloud-native logging (a managed, centralized audit trail) is harder for a compromised function to suppress, since the function itself typically has no permission to modify the log service's records.
- Information disclosure: a managed storage or database service is reachable from outside the traditional network perimeter by design (that is how it is managed), so a misconfigured access policy is directly internet-reachable in a way an on-premises database behind a firewall was not.
- Denial of service: on-premises capacity is fixed and a flood is visibly resource-exhausting; a serverless system auto-scales, so a flood instead becomes a cost-exhaustion and rate-limit problem (an attacker driving invocation counts, and therefore cost, rather than crashing a fixed pool of servers).
- Elevation of privilege: on-premises, privilege escalation often means compromising a host to gain broader network access; in a cloud tenant, it means a function's IAM role being usable to reach further than the function's actual job requires, since the role itself is the privilege boundary.
Recommended mitigation patterns
- Least-privilege IAM per function, scoped to specific resources rather than resource types, so a single function's compromise or misuse has the smallest possible blast radius.
- Explicit input validation at every event source, treating every trigger (API gateway request, queue message, storage-upload event) as untrusted input regardless of where it appears to originate, since the event source is the new perimeter.
- Resource-level policies on every managed service (bucket policies, queue access policies, database network rules) reviewed as part of the same threat model as the code, not as a separate infrastructure concern owned by a different team.
- Rate limiting and budget alerts to convert the denial-of-service and cost-exhaustion risk from an open-ended liability into a bounded one.
- Short-lived, scoped credentials (temporary security tokens rather than long-lived keys) wherever a function needs to call another service, so a leaked credential has a short useful life for an attacker.
Observability practices
- Centralized, tamper-resistant logging across every function and managed service, since there is no single host to install a traditional log agent on; the log aggregation has to be architected in from the start, not bolted on.
- Per-function invocation and permission-usage monitoring, specifically watching for a function exercising permissions it holds but has never used before, which is a strong signal of misuse of an over-permissioned role.
- Distributed tracing across event-driven chains, since a single user action can now trigger a chain of functions across multiple event sources, and a security-relevant anomaly (an unexpected fan-out, an unexpected downstream call) is only visible if the whole chain is traceable, not just each function in isolation.
Worked example
Consider a serverless image-processing pipeline: users upload images to object storage, a storage-upload event triggers a function that processes the file, and the processed result is written to a second storage location. Walking the four attack surfaces: the event source is the object-storage upload trigger, which fires on any file landing in the bucket, so if the upload endpoint is public-facing, the function must treat every uploaded file as untrusted, including its file type, size, and embedded metadata, not just its declared content-type. The function itself needs an IAM role scoped to read from the input bucket and write to the output bucket only, not broad storage access; if the processing library it uses has a known parsing vulnerability, a maliciously crafted image is the delivery mechanism, and the function's narrow role is what limits what that vulnerability can actually reach. The IAM role is the concrete mitigation surface: a role scoped to two specific buckets, rather than "storage:*", means that even a fully compromised function cannot read unrelated data in the account. The managed service boundary is the storage service's own bucket policy: it must reject uploads from outside the expected source and cap object size, since an attacker who can upload arbitrarily large or numerous files can drive both processing cost and, if the function scales without a concurrency limit, a real resource-exhaustion condition. Observability closes the loop: per-invocation monitoring on this function would catch it suddenly attempting to read from a bucket outside its normal two, which is the signal that either the role is misconfigured or the function's logic has been abused in a way the code review missed.
Trade-offs and pitfalls
- The most common wrong turn is threat-modeling a serverless system with a VM-based mental model, focusing on network perimeter and host hardening when there is no host, and missing that the real perimeter has moved to IAM policy and event-source validation.
- Over-permissioning IAM roles "to avoid breaking things during development" and never tightening them later is the single highest-leverage mistake, precisely because the absence of a host-compromise step means the role is the only thing standing between a function's weakness and a wide blast radius.
- Treating managed-service configuration (bucket policies, queue policies) as an infrastructure team's problem separate from the application threat model misses that these configurations are now part of the application's actual security boundary, not background plumbing.
- Under-investing in observability because "serverless has no servers to monitor" is a real trap: the lack of a host does not reduce the need for visibility, it changes what needs visibility, from host-level logs to per-invocation, per-permission, and cross-function trace data, and skipping that investment leaves the denial-of-service and privilege-misuse patterns above effectively invisible until real damage is done.
Design a threat model for a serverless data processing pipeline (API Gateway -> Lambda -> Kinesis -> analytics). Identify threats including event injection, function impersonation, excessive privileges, insecure dependencies, and cold-start related timing risks. Recommend mitigations across IAM, VPC placement, input validation, dependency management, observability, and secure deployment patterns.
Sample Answer
Direct answer
Model the pipeline (API Gateway into Lambda into Kinesis into downstream analytics) as a chain of trust boundaries where every hop independently verifies what the previous hop handed it, rather than inheriting trust from an earlier check. The five named threats each trace back to a different root cause, an unvalidated event, a spoofed or unauthorized invocation, an over-broad permission grant, a vulnerable packaged dependency, and a cold-start-related timing effect, so each gets addressed primarily through one or two of six mitigation categories (Identity and Access Management, or IAM; network placement; input validation; dependency management; observability; and secure deployment patterns) rather than one blanket fix covering everything.
Structured elaboration
flowchart LR
CLIENT[External client] -->|1: request| APIGW[API Gateway:\nschema validation,\nrate limiting]
APIGW -->|2: validated event| LAMBDA[Lambda:\nscoped IAM role,\ninput re-validation]
LAMBDA -->|3: PutRecord\nrestricted to this role| KINESIS[(Kinesis stream)]
OTHER[Other internal service\nno PutRecord permission] -.->|blocked by IAM| KINESIS
KINESIS -->|4: consume| ANALYTICS[Analytics Lambda:\nverifies producer marker,\nnarrow downstream scope]
ANALYTICS -->|5: scoped call only| DOWNSTREAM[Downstream service]
subgraph TB1[Trust boundary: public internet to platform]
CLIENT
end
subgraph TB2[Trust boundary: platform-internal, still\nper-function least privilege]
APIGW
LAMBDA
KINESIS
OTHER
ANALYTICS
DOWNSTREAM
end
Event injection or malformed payloads. Addressed primarily through input validation: enforce strict schema validation at API Gateway before a request ever reaches Lambda, rejecting unexpected fields, wrong types, or oversized payloads with a fail-closed default. For events written directly to Kinesis by internal producers rather than through the public API, add a signed or keyed marker that only an approved producer can generate, so a consumer can distinguish a genuinely produced event from one written by a compromised internal service, which schema validation alone cannot do.
Function impersonation. Addressed primarily through IAM: give each function a narrowly scoped execution role rather than a shared one, restrict who may invoke or update a given function to specific, named principals rather than any authenticated caller, and prefer short-lived, automatically rotated credentials over long-lived keys. Pair this with observability that flags an invocation or update coming from an unexpected principal, since that is exactly the signal that would indicate impersonation succeeded despite the IAM controls.
Excessive privileges. Also primarily an IAM concern, but distinct from impersonation: scope each function's execution role to the specific resources it actually touches, a specific Kinesis stream's Amazon Resource Name (ARN), a specific storage prefix, a specific secret, rather than account-wide or wildcard access, and keep the role that deploys or updates a function separate from the role the function uses at runtime. A function that only needs to read one stream and write to one downstream service should be structurally unable to reach anything else, so that a bug or a successful exploit inside the function's own code cannot be leveraged into a broader compromise.
Insecure dependencies. Addressed through dependency management: generate a software bill of materials (SBOM) at build time covering both direct and transitive dependencies, run software composition analysis (SCA) scanning against a maintained vulnerability feed before a package is allowed to ship, pin dependency versions rather than floating on the latest release, and keep the runtime's dependency footprint minimal since a smaller set of packages is a smaller set of things that can go wrong. Combine this with secure deployment patterns: immutable, versioned deployment artifacts (no editing a deployed function in place) and signed builds, so a dependency that passed the scan cannot be silently swapped for something else afterward.
Cold-start related timing risks. This threat actually covers two distinct risks worth separating. The first is a timing side channel: if a security-sensitive check (verifying a signature or comparing a secret) behaves differently on a cold start than on a warm invocation, an attacker who can distinguish cold from warm responses might infer something about the check's internal state; the mitigation is using constant-time comparison for any security-sensitive check and avoiding branching on cold-versus-warm state in code paths that handle secrets. The second is availability: an attacker who triggers a large burst of concurrent, unique invocations can force many simultaneous cold starts, degrading latency or throughput for legitimate callers even without exploiting anything directly. The mitigation here is a mix of reserved concurrency (capping how many concurrent instances a function can consume, containing the blast radius on shared infrastructure) and, for functions where consistent low latency is itself a security requirement, provisioned concurrency (keeping a set number of execution environments warm in advance), plus rate limiting at API Gateway so an invocation flood is throttled before it ever reaches Lambda.
Network placement. Functions that need to reach resources with no public endpoint (an internal database, a private service) should run inside a private subnet, connecting to AWS services like Kinesis through an interface Virtual Private Cloud (VPC) endpoint (powered by AWS PrivateLink) so that traffic never traverses the public internet. Functions that only call public AWS service endpoints can stay outside a VPC entirely, avoiding network overhead that provides no security benefit for a function with nothing private to reach; VPC placement is a targeted control for functions that actually need it, not a default applied uniformly regardless of what a given function talks to.
Observability, as its own design-time requirement. Beyond the impersonation-specific signal above, the pipeline as a whole needs tracing and metrics that let an operator reconstruct what happened across all three hops, API Gateway, Lambda, and Kinesis, for a given request or event: a propagated request identifier carried through every hop, per-function invocation and error-rate metrics, and Kinesis-level metrics such as iterator age (how far a consumer has fallen behind, which is itself a signal of a possible tampering or throttling issue upstream). This is design-time work in the same sense as the rest of this answer: specifying which signals the pipeline must emit so that any of the five threats above leaves a visible trace, not building the alerting and response system that consumes those signals, which is a separate discipline.
Secure deployment patterns, beyond the dependency-specific case above. The same discipline applies to the pipeline's own configuration and infrastructure, not only to application dependencies: manage infrastructure as code with a required review step, roll out changes to the pipeline (a new IAM policy, a new event-source mapping) through a staged or canary process rather than applying a change to the whole pipeline at once, and pair every deployment with an automated rollback trigger tied to the observability signals above, so a bad change is caught and reverted from its own telemetry rather than from a manual report.
Worked example
Suppose the analytics Lambda, on certain event types, calls a downstream administrative service to update account settings, a legitimate but sensitive part of the pipeline. An attacker who has compromised a low-privilege internal service, not the analytics Lambda itself, attempts to write a crafted event directly onto the Kinesis stream that looks like a legitimate "apply this admin action" event, trying to trigger that sensitive downstream call without going through the public API at all. In the intended configuration, only the specific producer Lambda's execution role has PutRecord permission on this stream; the compromised low-privilege service lacks that specific IAM grant, so the write attempt is denied at the IAM layer before the crafted event ever reaches the stream, let alone the analytics Lambda. Now consider a worse case: the attacker has instead compromised a service that legitimately does have PutRecord access to this stream. IAM alone no longer stops the write, but the analytics Lambda's input validation checks every consumed record for the signed producer marker described above; a record written by an unapproved source, even one with valid write access to the stream itself, fails that check and is discarded rather than acted on. If that check were somehow also defeated, the analytics Lambda's own execution role is scoped narrowly to the one downstream administrative action it actually needs, not a broad administrative capability, so even a fully successful forged event has a bounded blast radius rather than an open-ended one. Three independent mechanisms drawn from two of the six mitigation categories, IAM (both the stream-write restriction and the analytics function's narrowly scoped execution role) and input validation (the producer-marker check), each reduce the same attack path on their own, which is the point: no single layer is asked to be the only thing standing between a compromised internal service and a sensitive downstream action.
Trade-offs and pitfalls
VPC placement has a real, if now much smaller, cost: Lambda functions in a VPC historically incurred a significant cold-start penalty from provisioning a network interface per invocation, and while AWS's 2019 Hyperplane networking change meaningfully reduced that overhead by pre-provisioning shared network interfaces instead of one per function, VPC placement is still not free, so it should be applied to the functions that genuinely need to reach private resources rather than as a default for every function in the pipeline. Provisioned concurrency reduces cold-start-driven timing and availability risk but incurs an ongoing cost for capacity that sits idle between invocations, so it is worth reserving for the specific functions where cold-start behavior is actually security-relevant or latency-critical, not applied blanket across the whole pipeline. A common pitfall in practice is that teams grant a broad execution role early "to get it working" during initial development and never tighten it once the function is stable; the fix is a build-time policy check (policy-as-code review that rejects IAM statements broader than the pipeline's own declared usage) rather than relying on someone remembering to revisit permissions later. Finally, a subtle but costly mistake is securing the public API Gateway entry point thoroughly while forgetting that Kinesis is itself a separate write surface: if any internal service can write to the stream without IAM restricting who may do so, the API Gateway's careful validation is bypassed entirely by construction, since the stream, not the gateway, is what the downstream consumer actually trusts.
Walk through a threat modeling exercise for a new cloud-native microservice that accepts file uploads and stores them in object storage. Use an explicit framework (e.g., STRIDE) to identify assets, actors, threats, attack paths, and mitigations. List the artifacts you'd produce (data flow diagram, threat list, prioritized mitigations) and one example detection control for a critical threat.
Sample Answer
Direct answer
A threat-modeling exercise for a cloud-native file-upload microservice produces three concrete artifacts: a data-flow diagram (DFD) that names every asset, actor, and trust boundary; a threat list built by walking each element of the DFD against STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege); and a prioritized mitigation list ranking those threats by realistic impact and likelihood. For this specific design, the highest-priority threat is Elevation of Privilege through the asynchronous processing worker, since a compromised file-processing step inherits whatever cloud permissions that worker's identity holds, and the concrete detection control below is built around exactly that threat.
Structured elaboration
Assets, actors, and the data-flow diagram. Assets: the uploaded file content itself, the object storage bucket it lands in, the metadata database recording upload and processing state, and the upload API's authentication tokens. Actors: the authenticated end user (legitimate uploader), an external attacker (unauthenticated, or holding a stolen or forged token), and two internal service identities, the upload API and the asynchronous processing worker, each of which holds its own cloud permissions.
flowchart LR
User[Authenticated End User]
Attacker[External Attacker]
subgraph Edge["Edge: public-facing"]
API[Upload API]
end
subgraph Internal["Internal: service network"]
Queue[[Processing Queue]]
Worker[Async Processing Worker]
MetaDB[(Metadata Database)]
end
subgraph Storage["Object Storage"]
Bucket[(Object Storage Bucket)]
end
User -->|upload request plus token| API
Attacker -.->|forged or stolen token| API
API -->|validated file| Bucket
API -->|enqueue job| Queue
Queue --> Worker
Worker -->|reads object| Bucket
Worker -->|writes result metadata| MetaDB
API -->|writes upload record| MetaDB
Threat list (STRIDE walked against the diagram above):
| STRIDE category | Threat | Where |
|---|---|---|
| Spoofing | Attacker uses a stolen or forged token to call the upload API as a legitimate user | Upload API edge boundary |
| Tampering | Uploaded object is modified after storage by an actor with broader-than-intended bucket write access | Object storage bucket |
| Repudiation | A user who uploaded malicious content denies doing so, with no verifiable record tying the upload to their authenticated session | Upload API to metadata database |
| Information Disclosure | Overly broad bucket policy, or an overly long-lived pre-signed URL, exposes stored files to unintended readers | Object storage bucket |
| Denial of Service | An attacker uploads very large files, many small files rapidly, or a decompression-bomb-style file that consumes excessive resources when the worker processes it | Upload API and processing worker |
| Elevation of Privilege | A malicious file exploits a vulnerability in the processing worker's file-handling logic (an image, document, or archive parser), and the worker's cloud identity has broader permissions than the processing task needs, letting the compromise reach other cloud resources | Async processing worker |
Attack path for the highest-priority threat. The Elevation of Privilege path runs: attacker uploads a crafted file that passes the upload API's basic validation (correct declared content type, acceptable size) but is actually built to exploit a parsing vulnerability in whatever library the worker uses to process it (image library, document parser, archive extractor); the worker picks the job off the queue, reads the object, and processing triggers the exploit; if the worker's cloud identity holds permissions beyond what processing strictly requires (for example, broad read/write across all buckets rather than just the one it processes, or permissions to call unrelated cloud application programming interfaces, APIs), the compromised worker process can pivot to reading or modifying data well outside the original upload's scope.
Prioritized mitigations, ranked by the combination of how likely the path is and how much damage it enables:
- Least-privilege identity for the processing worker (addresses Elevation of Privilege, ranked highest because it is the one threat here whose worst case is otherwise unbounded: every other entry on the list has a blast radius confined to one upload, one bucket, or one log record, while a compromised worker holding broad cloud permissions reaches resources that have nothing to do with file uploads at all. Ranking it first is not the same as it being sufficient, and it is worth saying which entries it does not touch: least-privilege scoping on the worker does nothing for Repudiation, nothing for Information Disclosure through an over-long pre-signed URL, and nothing for resource exhaustion, which is why items 2 through 5 are requirements rather than nice-to-haves): scope the worker's cloud identity to only the specific bucket paths and operations processing requires, with no broad cross-bucket or administrative permissions.
- Content validation beyond declared type (addresses Elevation of Privilege and Denial of Service): validate actual file content (magic-byte/content sniffing, not just the client-declared content type or file extension), enforce size limits before the file is fully accepted, and guard against decompression bombs by capping expanded size during any extraction step.
- Short-lived, narrowly scoped upload tokens and pre-signed URLs (addresses Spoofing and Information Disclosure): tokens tied to a specific authenticated session with a short expiry, and any pre-signed URLs generated for reading objects scoped to minutes, not days.
- Bucket policy least privilege plus encryption (addresses Information Disclosure and Tampering): default-deny bucket policy with explicit, narrow grants, and server-side encryption so a misconfigured policy is not the only line of defense.
- Signed, immutable audit logging of upload events (addresses Repudiation): record each upload tied to the authenticated identity and a content hash, in a log the uploading service itself cannot retroactively edit.
Worked example
One example detection control for the highest-priority threat, Elevation of Privilege via the processing worker: alert on any API call made by the processing worker's cloud identity that falls outside its expected, narrow allow-list, most importantly any call touching a bucket other than the one it is scoped to process, or any call to an unrelated service (identity and access management, compute control-plane APIs, and so on). Because the least-privilege mitigation above already constrains what the worker's identity is supposed to be able to do, any call outside that expected set is a strong, low-noise signal, not a fuzzy heuristic: a correctly-behaving worker should never generate one. Concretely, this means shipping the cloud provider's own API audit log (for example, an AWS-style CloudTrail equivalent) for the worker's service identity to a monitoring pipeline with a rule that fires the moment that identity's calls deviate from its documented allow-list, which catches exactly the pivot step in the attack path above (the compromised worker attempting to read or write outside its intended scope) even if the initial exploit itself was never directly observed.
Trade-offs and pitfalls
The most common mistake is validating only the client-declared content type or file extension and treating that as sufficient input validation; an attacker fully controls both of those fields, so real validation has to inspect actual file content. A second is scoping the worker's cloud identity broadly "to avoid permission issues later," which is precisely the choice that turns a contained parsing-library exploit into a cross-resource compromise; least-privilege scoping has real operational cost (more explicit configuration, more friction when the processing logic legitimately needs a new resource) but that cost is the point, since it forces each new permission to be a deliberate decision rather than a default. A third pitfall is treating the DFD, threat list, and mitigation list as one-time deliverables produced once at design time and never revisited; this pipeline's processing logic and dependencies will change, and a new library version or a new processing step reopens the STRIDE walk for at least the elements it touches, not the whole system from scratch, but not nothing either.
As an Information Security Analyst, perform threat modeling for a cloud-native service running in Kubernetes. Identify the top five attack vectors specific to containers and orchestration (e.g., image supply chain, misconfigured RBAC), and recommend concrete mitigations you would implement both in CI/CD and at runtime.
Sample Answer
Direct answer
Threat modeling a cloud-native service running on Kubernetes (an open-source system for orchestrating containerized applications across a cluster of machines) means walking the whole lifecycle, not just the running cluster: what gets built and signed before deployment, what the cluster's own control plane and configuration allow once it's running, and what a workload can reach if it's compromised. The five attack vectors below cover that full lifecycle deliberately, because a threat model that only looks at runtime configuration misses the supply-chain and configuration threats that are usually cheaper for an attacker to exploit than anything at runtime.
Structured elaboration
The method: lifecycle-wide attack surface, not just the running cluster
Walk the service's full lifecycle in order rather than starting from the running cluster's configuration: what gets built (the image and its dependencies), what gets signed and verified before it ships, what the cluster's control plane and configuration allow once the workload is running, and what a compromised workload can reach from there (secrets, other workloads, the underlying host). For each stage, ask what a preventive control in continuous integration/continuous delivery (CI/CD, the automated pipeline that builds, tests, and ships code) would catch before deployment, and separately what a runtime control would catch or contain once the workload is live, since neither alone covers the full lifecycle. Applying that method to a concrete cloud-native service is the worked example below.
Worked example
Top five attack vectors, each with CI/CD and runtime mitigations
-
Image supply-chain compromise. A malicious or tampered container image reaches production, either through a compromised base image, a poisoned dependency, or a build pipeline that was itself compromised.
- In CI/CD: sign every image (a tool like Cosign is a common choice) and verify provenance before it's allowed to deploy; scan images for known vulnerabilities and block builds above an agreed severity threshold; enforce immutable, content-addressed tags rather than mutable tags like
latestthat can silently point to a different image over time. - At runtime: an admission controller (a component that intercepts and can reject requests to the cluster's API before they take effect, commonly implemented with Open Policy Agent Gatekeeper or a similar policy engine) enforces that only signed images from an approved registry can actually run.
- In CI/CD: sign every image (a tool like Cosign is a common choice) and verify provenance before it's allowed to deploy; scan images for known vulnerabilities and block builds above an agreed severity threshold; enforce immutable, content-addressed tags rather than mutable tags like
-
Misconfigured Role-Based Access Control (RBAC) and excessive privileges. A workload's service account, or a human operator's role binding, grants far more access than the workload actually needs, so a single compromised pod can act far beyond its intended scope.
- In CI/CD: static analysis of Kubernetes manifests catches overly broad role bindings before they merge; require least-privilege templates as the default rather than the exception, and specifically disallow binding a
ClusterRole(a cluster-wide permission set) to anything other than a small, explicitly reviewed set of administrative identities. - At runtime: keep each workload's service account bound to a narrowly scoped
Rolerather than aClusterRole, and setautomountServiceAccountToken: falseon pods that never call the Kubernetes API at all, so there is no token sitting in the pod to steal; monitor the cluster's audit logs for API calls that look anomalous for a given service account's normal behavior.
- In CI/CD: static analysis of Kubernetes manifests catches overly broad role bindings before they merge; require least-privilege templates as the default rather than the exception, and specifically disallow binding a
-
Secrets leakage. Credentials, API keys, or certificates end up somewhere they shouldn't: baked into an image layer, committed to a repository, or exposed in a pod's environment variables where any process in that pod (or a debugging tool with pod access) can read them.
- In CI/CD: secret-scanning on every commit blocks plaintext credentials from ever merging; secrets are injected at deploy time from a dedicated secrets manager rather than baked into the image or hardcoded in a manifest.
- At runtime: mount secrets through a Container Storage Interface (CSI) secrets driver rather than plain Kubernetes Secrets objects where stronger guarantees are needed, encrypt the cluster's underlying etcd datastore (the key-value store holding all of Kubernetes' cluster state, including Secrets objects, at rest), and rotate credentials on a defined schedule rather than leaving long-lived keys in place indefinitely.
-
Container escape and host compromise. A vulnerability in the container runtime, an overly permissive container configuration, or a kernel-level flaw lets a process inside a container break out and reach the underlying host or other containers on the same node.
- In CI/CD: build from minimal, hardened base images (a distroless image, containing only the application and its runtime dependencies with no shell or package manager, meaningfully shrinks what an attacker who does get code execution can do next) and explicitly drop unneeded Linux capabilities in the pod specification rather than accepting the runtime's defaults.
- At runtime: enforce the restricted Pod Security Standard through Pod Security Admission (the built-in admission controller that rejects pod specs requesting privileged mode, host namespaces, or host path mounts), run containers as a non-root user, apply a seccomp profile (restricting which system calls a container's processes can make) and a mandatory access control profile such as AppArmor, and instrument runtime behavioral monitoring (a tool like Falco is a common choice) to detect syscall patterns consistent with an escape attempt.
-
Lateral movement between workloads. Once any single workload is compromised, a flat cluster network lets the attacker reach every other service in the cluster with no additional barrier.
- In CI/CD: define default-deny network policy templates for every namespace as a baseline, so a new service starts with no implicit network access to anything else and has to explicitly declare what it needs to reach.
- At runtime: enforce Kubernetes NetworkPolicies (rules restricting which pods can communicate with which others) as the default-deny baseline described above, and consider mutual Transport Layer Security (mTLS) between services via a service mesh (Istio or Linkerd are common choices) for identity-based, encrypted service-to-service communication rather than relying on network location alone.
Monitoring and incident response, tying the vectors together
Centralizing logs and metrics from the cluster's control plane, the container runtime, and the admission controllers into one place is what makes the five vectors above actually detectable in practice rather than just theoretically covered: alert on image or policy violations caught by the admission controller, anomalous API or audit events, and privilege-escalation attempts. Maintain a specific playbook for each of the higher-severity scenarios (a compromised image reaching production, a leaked secret, a suspected cluster breach) rather than a single generic incident-response document, since the first containment step differs meaningfully between them.
Trade-offs and pitfalls
- CI/CD controls and runtime controls are complementary, not substitutes for each other. A build pipeline that blocks unsigned images is only as strong as the admission controller enforcing the same rule at deploy time; skipping either half leaves a real gap, since a determined attacker who can bypass the pipeline (a compromised CI credential, for example) faces no second check if the runtime side was never configured.
- Default-deny network policy is a real operational cost, not just a configuration flag. Every legitimate service-to-service dependency has to be explicitly declared, which means the policy has to be maintained as the architecture evolves; a stale, overly narrow policy breaks production traffic just as surely as a stale, overly broad one leaves an opening.
- Signing and provenance verification add friction to the build pipeline (key management, verification steps, occasional build failures from a misconfigured signature) that a team under deadline pressure will be tempted to bypass "just this once," which is exactly the moment the control is most needed.
- Common wrong turn: treating Kubernetes' own RBAC and network policy primitives as sufficient on their own without also addressing the supply-chain vectors (image signing, secret scanning) that get an attacker into the cluster in the first place; a cluster with excellent runtime hardening and no image provenance checking is still trusting whatever the build pipeline hands it.
Unlock Full Question Bank
Get access to all 9 Threat Modeling and Attack Surface Analysis interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.