In an event-driven cloud workload, why is a queue message an untrusted entry point?
answer
- ask who can produce the event
- the trigger binding is the access control
- internal-looking is not internal
- resource policy is the only gate
- re-authorise in the consumer
basics
~20 sWhoever the queue's access policy permits to send can invoke the consumer with a payload of their choosing. The trigger binding is the only access control on that path, so an event source is an entry point like any public endpoint.
solid answer
~50 sAn entry point is anywhere data crosses into the system from a source you do not control, and in an event-driven design the trigger binding is the access control. If a queue's resource policy allows any principal to send, anyone who learns the queue identifier can hand my consumer a message, and the consumer treats it as pre-validated because it looks internal. Take a claims flow where an intake account validates submissions and enqueues them and an adjudication account consumes and pays: a permissive queue policy lets an outside principal inject a claim that never passed intake. That is spoofing of the producer, and it also destroys audit truth, because a paid claim now has no intake record behind it. So I enumerate every trigger, ask who can produce that event, tighten the policy to the one producer role, and re-authorise in the consumer.
go deeper
Know that a queue message or a file-upload event is input from outside, just like an HTTP request, and that the consuming code has to validate it rather than assume an earlier step did.
Be able to explain that the event source's resource policy is the access control on that path, and to name the threats an injected message lands: impersonating the producer and tampering with fields the consumer trusts.
Show the enumeration habit at a whiteboard: walk every trigger binding, state who can produce each event, and identify which consumer assumptions break when that set is wider than the team believes.
Own the pattern across teams: decide that no consumer may treat producer identity as implied by delivery, and that event-source policies are reviewed as an exposed surface rather than as internal plumbing.
## The definition that does the work An **entry point** is any place where data crosses into the system from a source the system does not control. In a request-response design the entry points are obvious, because they are endpoints someone deliberately exposed. In an event-driven cloud design they are configuration: a function is bound to a queue, a topic, an object-created notification, a schedule or a stream, and from then on **anything that can produce that event can invoke that code with a payload of its choosing**. Nobody wrote a listener, nobody opened a port, and there is no request log that looks like an attack. That is exactly why these entry points get missed. The second half of the definition matters just as much: *from a source the system does not control*. The control is not the network, and it is not obscurity. It is the **resource policy on the event source**, plus whatever the producer had to prove to write there. If that policy is permissive, the event source is a public entry point that simply lacks a URL. ## Worked example: a claim that never passed intake An insurer runs intake in one account and adjudication in another. Intake accepts submissions, validates them, checks the policy is in force, and puts a message on a queue. The adjudication account consumes messages and approves payment. The team's mental model is a pipeline: adjudication trusts the message because intake produced it. Now suppose the queue's policy grants send permission broadly rather than to the intake role alone. An external principal who has learned the queue identifier - from a leaked configuration file, a support ticket, a former contractor, an error page - can put a well-formed message directly onto the queue. The threats that lands are: - **Spoofing** (violating authenticity): the message impersonates intake. The consumer has no way to tell the difference, because *being on the queue* was the whole proof of origin. - **Tampering** (violating integrity): fields the consumer trusts because intake supposedly set them - a validated flag, an approved amount, a policy identifier - are now attacker-chosen. - **Repudiation** (violating non-repudiation): a payment goes out with no intake record behind it. When someone reconstructs what happened, the audit trail says a claim was adjudicated and no submission exists. The asset at risk here is not only money, it is the truthfulness of the record. Notice what is *not* the problem. The queue is not leaking data, the network is not misconfigured, and no code has a memory-safety bug. The defect is entirely in where the trust was placed. ## Enumerating event entry points A repeatable pass over a serverless design: 1. **List every trigger binding.** For each compute unit, what can invoke it - a queue, a topic subscription, an object notification, a schedule, a stream, a direct call? 2. **For each one, ask who can produce that event.** Read the resource policy of the source, not the code of the consumer. The answer is a set of principals; write it on the diagram. 3. **Ask what the consumer assumes about the producer.** Any field it does not re-check is a field the producer set on its behalf. 4. **Ask whether the event can be replayed or duplicated.** Delivery is usually at-least-once, so a consumer that is not idempotent can be made to act twice by a replayed message. The fourth point is worth separating from trust: at-least-once delivery is a reliability property, not an attack, but it means *correct* behaviour under duplicates is already required, and an attacker who can send gets that lever for free. ## What actually fixes it - **Least-privilege resource policy on the source.** Name the producer role. This is the control that makes the event source non-public in the first place. - **Authorise in the consumer.** Treat the message as untrusted input: validate the schema and the values, and check the claim against state you own rather than accepting what the payload asserts. - **Carry provenance you can verify.** Correlate the message to a record that only the real producer could have created - for example, look up the intake record by its identifier and confirm its state, rather than believing the message's *validated* flag. - **Separate the identity of the producer from the fact of being on the queue.** *It arrived here, therefore it is ours* is the assumption being exploited. ## Where this generalises The same reasoning applies to an object-created event. If a bucket prefix accepts uploads through presigned URLs, the writer of that object is an **anonymous internet client** holding a URL, and the entire access control is that URL's scope and expiry. Anything the consuming function then does with the bytes - parse them, transcode them, feed them to a downstream step - is processing attacker-supplied input, even though no part of the picture looks like a web endpoint.
- A transcoding function writes its output back into the same prefix whose object events trigger it. What have you drawn?A cycle. The function is now an external entity at its own entry point: each output object fires another event, which produces another object. On the diagram it is an arrow that loops back, and the assets it puts at risk are availability and spend rather than data. Fixes are structural - write to a separate prefix or bucket, filter the trigger by suffix or prefix, and make the step idempotent with a stop condition.
- Isn't the queue identifier effectively secret, so injection needs an insider?No. Identifiers show up in configuration files, client bundles, log lines, error messages, support tickets and documentation, and they are not rotated when someone leaves. Treating an identifier as an authorisation check is security by obscurity: the resource policy, not the difficulty of guessing a name, decides who can send.
- How do you find the event entry points in a system nobody documented?Enumerate from the compute side: for each function or task, list its trigger bindings, then read the resource policy of each source to get the principal set that can produce that event. That gives you the entry points and their attacker positions in one pass, which is more reliable than asking the team which endpoints they think are public.
saying these in an interview costs you the question
- Assumes a message on an internal queue was already validated upstream
- Treats only HTTP endpoints as entry points
- Believes knowing the queue identifier is what protects it
- Trusts envelope fields such as a validated flag without checking state
- Assumes same organisation or same account implies same trust level
- Confuses at-least-once delivery with an attack rather than a required behaviour