KevinBytes
Back to writing
4 min readArchitecture

Using C4 Diagrams In Security Work

Follow a customer attachment from the browser to storage, using C4 to locate the ownership check and the paths that might bypass it.

By Kevin O'Connor

Last reviewed

A box labeled “backend” doesn't help much when you're trying to work out who can download a customer's attachment. Somewhere inside that box, a request becomes a storage read. The important part is where the application decides that the user owns the ticket, and which credential it uses after that decision.

This is the kind of question I like C4 for. You can start with the people and systems involved, then zoom in until the access decision is visible. You don't have to put the whole implementation on one page.

The C4 model has four levels: system context, containers, components, and code. Context and container diagrams are often enough. I add detail when it helps explain a control or an access path that would otherwise be hidden.

Start with the attachment's owner

Imagine a support application where customers upload attachments to tickets and support staff retrieve them. Its context view might look like this:

[Customer] ------> [Support System] <------ [Support Analyst]
                         |
                         v
                  [Identity Provider]

At this level, I'd describe who each user is and what they use the system for. A customer works within their own account. A support analyst may work across accounts under a different policy. The identity provider authenticates users, while the support system owns the rules for access to tickets and attachments.

That last distinction matters. A valid login doesn't tell the application which ticket a person may read. Put that responsibility in the description so the next diagram has something specific to explain.

Show the applications and stores

In C4, a container is an application or data store. It might be a browser application, an API, a worker, or a database. It doesn't specifically mean a Docker container.

For this support system, a container view could show:

[Browser App] --HTTPS, session--> [Support API]
                                      |
                    +-----------------+-----------------+
                    |                                   |
                    v                                   v
          [Ticket Database]                    [Attachment Store]
       account and ownership data                  file content

The API has access to both the ownership data and the files. Now there is a concrete path to review: the browser supplies an attachment identifier, the API checks its relationship to the user's account, and the storage request uses the resulting authorized object key.

I'd label the connections with the data and identity they carry. “SQL” alone doesn't tell me whether the database sees an individual user or a shared application role. “HTTPS” tells me about transport, but leaves the storage credential unexplained.

Keep load balancers, subnets, and host placement in a deployment view when those details matter. Otherwise a review of attachment ownership can disappear into a discussion of infrastructure before anyone has located the ownership check.

Find the check inside the API

A component view makes the API's responsibilities more explicit:

[Download Handler] --> [Session Validation]
        |
        v
[Attachment Access Check] --> [Ticket Repository]
        |
        v
[Storage Client] --> [Attachment Store]
        |
        v
[Download Audit Event]

I'd follow a request for another customer's attachment through this path. Does the access check use the account from the authenticated session? Does it check the attachment's relationship to the ticket as well as the ticket's owner? Can the caller substitute a storage key after authorization?

Then I'd look for callers the drawing left out. A background export job may call the storage client directly. That job needs its own authority and tenant scope; it can't rely on a browser session check it never passes through. Add it to the diagram, including how its work item was authorized.

An expiring download URL is another branch worth showing if the design uses one. Once the URL leaves the API, access depends on the storage service's handling of that URL. Its lifetime and any revocation limits become part of the access decision.

Use the diagram during the review

A diagram should make it easier to find the relevant code and configuration. Put the component names used in the repository on the drawing where possible. If the access check lives in a shared module, identify that module. If only one handler uses it, the diagram shouldn't imply broader coverage.

For denied downloads, find the record that shows the requested object and rejection. For successful ones, check how the user action connects to the storage request. That helps during an incident when you need to separate an attempted access from a completed download.

Use a small annotation key. Dashed boundaries and explicit labels are easier to carry across tools than a complicated color scheme. Color can reinforce the distinction, but readers should still be able to understand a monochrome copy. An “internal” label describes location; it doesn't grant a service permission to read every attachment.

Code-level diagrams earn their place when the implementation is difficult to follow, such as a policy evaluator with several entry points. For a straightforward handler, a link to the function and a test is often more useful than another drawing.

Keeping it current

Store the diagrams with an owner and the release or deployment they describe. Review them when an access path changes. Adding a worker, export feature, or new storage credential should prompt a check of the affected paths.

For the attachment example, I would keep one authorized download and one cross-account denial beside the diagrams as repeatable cases. If a new worker can retrieve the same file, add its case too. That gives the drawing a continuing use after the architecture meeting ends.

Email updates

Get new research by email

In-depth notes on AI security, threat research, and practical defensive work.

Confirm your email to subscribe. Occasional research updates, with an unsubscribe link in every email.