Security thinking
Draw trust boundaries
Mark where data or authority crosses between browsers, servers, databases, third parties, administrators, and local machines.
A trust boundary is a place where data or authority changes hands. Most security bugs appear because code trusts something too early.
The browser and server are different trust zones. So are your server and a webhook provider, or your application and a package downloaded during a build. Validate the crossing even when both systems belong to you.
Find the boundaries in a real request
Follow one note-creation request through a typical stack:
flowchart LR
accTitle: Trust boundaries in a note creation request
accDescr: A browser crosses the public network with a session cookie and note data, a load balancer forwards the request, and the application uses database credentials to reach Postgres.
Browser -->|"Session cookie<br/>note title and body"| Balancer["Load balancer"]
Balancer -->|"Forwarded request"| App["App server"]
App -->|"Database credentials<br/>validated note"| Postgres
Data crosses at least three boundaries here. The browser sends a title and body you must not trust. The session cookie must map to a real user before anything acts on it. The app talks to Postgres with credentials that prove it is the app, not some random process on the network.
Two requests, two proofs
Now add a webhook from your payment provider. It reaches the same server as a browser request, but it carries a provider signature instead of a user session:
POST /api/notes proof: session cookie -> user identity
POST /webhooks/payments proof: HMAC signature -> provider identity
Treating both requests alike can grant the webhook the wrong authority. A webhook has no user session, so code that reads “the current user” from it either crashes or, worse, falls back to a default account. Each boundary has its own kind of proof, and the server must check the right one.
Label what crosses, not just the boxes
A diagram with boxes but no crossing data is decoration. For each arrow, label three things: the identity, the payload, and the credential that cross. “Browser to server: anonymous or session user, JSON note body, session cookie” tells you exactly what to verify at that line.
Then test one crossing with the proof removed. Send the note-creation request without the session cookie. Send the webhook without its signature header. Both should fail with a clear denial. If either succeeds, you found code trusting a boundary it never verified — exactly the class of bug this exercise exists to catch.
Lesson completed