You think about how things should work. Attackers think about how things could work. The gap between those two views is where vulnerabilities live.
Take a file upload endpoint. You think: "The user picks a profile photo and we save it."
An attacker thinks:
- What if I upload 10 GB?
- What if the filename is
../../etc/passwd? - What if it's actually an HTML file with JavaScript?
- What if I skip the content-type header entirely?
Make "what if?" a habit.
Three questions before you ship
- Who can reach this? Public? Authenticated? Admin? Can someone bypass the gate?
- What data does it touch? Credentials? PII? Payment info? Sensitivity determines severity.
- What if the input is hostile? Not broken by accident. Crafted with intent.
Least privilege
Give every component the minimum access it needs.
- Read-only page? Read-only database connection.
- Frontend API key? No admin scope.
- Email background job? No access to billing data.
When something breaks - and it will - least privilege limits the blast radius.
Defence in depth
No single control is perfect. Stack them.
- Input validation catches garbage at the boundary.
- Parameterised queries stop SQL injection even if validation misses something.
- Output encoding stops XSS even if bad data reaches the template.
- CSP limits what scripts run even if encoding fails.
There is no "secure"
Only "secure enough, for this context, against these threats, right now."
A personal blog and a banking app have different threat models. Invest accordingly.
Habits
- Read error messages. Stack traces leak information. Error handling is a security surface.
- Question defaults. Frameworks ship with sensible defaults. Usually. Know the gaps.
- Think about state. Who set this value? Can it be tampered with?
- Assume hostile networks. Even internal ones. TLS everywhere.
- Log what matters. When something bad happens, will you know who did it?
The takeaway
Security mindset isn't fear. It's discipline. Ask "what if?" early and often.
