Security Guides
Common security mistakes are failures of assumption, not of skill — four beliefs that keep breaking
Real incidents rarely involve exotic attacks. Sorted by the assumption behind them, the causes come down to four beliefs — and each was true once, which is why nobody re-examines them.
Line up the incidents that actually happen and exotic attack techniques barely appear. What appears is a small set of recurring shapes — and those shapes are clearer when sorted not by technology but by the assumption behind them.
① "Defaults must be safe" — nothing was configured
Defaults chosen for ease of development
detailed errors · admin surfaces reachable · broad permissions
↓ shipped unchanged
A signpost for whoever is looking
structure, paths, versions and internal values become readable
- Debug output enabled in production
- Do error pages expose environment variables, file paths or stack traces? Open a URL that does not exist and look at what comes back with your own eyes. Framework specifics live in the framework guides
- Administrative surfaces reachable
- Database management tools and admin consoles reachable from the internet. Do not rely on authentication alone — layer IP restrictions or network-level blocking
- Headers unset, or copied from a stale list
- See which headers still matter and which do not. A deprecated header can do harm if you add it
- Weak storage left in place
- Password storage as described in hashing and salting. "It's encrypted" is not a description of a storage scheme
- Updates have stopped
- Running a language, framework or CMS past its supported life. See machine-monitoring dependencies and what end of support actually means
② "Unused things must be harmless" — nothing was closed
Attack surface grows out of what you retired, not what you built. The moment something stops being used, it quietly drops out of monitoring and review.
- Secrets left in a public directory
.env, backups, dumps, config files. what must never sit in a public directory / placement on shared hosting- Storage still public
- Turning blocking on does not delete the existing public policy. public buckets
- Dangling DNS records
- Delete a resource without the DNS entry and anyone can claim the name and take the subdomain. subdomain takeover
- Registration and admin paths left open
- Is open registration still enabled? Measure it over HTTP rather than reading the config (authentication vs authorization). Do not leave a guessable URL as the only thing "hiding" a page
- Accounts you thought were cancelled
- Cancelling a service and deleting the account are different, and data stays until you delete. when your hosting provider is breached
- Old keys, tokens and permissions
- Departed colleagues' accounts, tokens with no expiry, over-broad scopes. SSH keys and least privilege / password managers
Do not scope the inventory to what is running
Limit your review to "things currently in use" and this shape becomes structurally invisible. Scope it to everything you ever created — subdomains, accounts, keys, buckets, staging environments, third-party subscriptions. Something you stopped using is still an asset until it is deleted. How to build the list: the asset inventory checklist.
③ "We would notice" — nothing is observable
Damage is determined less by the breach itself than by how long it runs before anyone notices. And most environments have no mechanism for noticing.
The usual state
・Access logs exist but nobody reads them
・No audit log of authentication attempts (so "what was seen" cannot be reconstructed afterwards)
・Rate limits are configured but nobody can see when they trigger
・Public status is the composition of several layers, so no single one answers the question
The end state is being able to say only "we don't know" about whether anything happened.
The minimum worth having
・A notification on new user registration (one signal is enough to catch the odd one)
・A record of authentication successes and failures, with a retention period
・Machine monitoring of dependency vulnerabilities (people cannot track this → osv-scanner)
・A record of limit hits, and an alert on a spike (rate limiting and abuse control)
You do not need all of it. One working path to noticing is what stops an incident from becoming a long one.
With no logs, the honest answer is "we don't know", not "nothing happened"
The most dangerous move in incident response is reading an absence of records as evidence that nothing occurred. With no records, the only supportable statement is "we don't know" — and in that case, proceed on the assumption of compromise and rotate whatever could have been exposed. The organisational floor is in the organisational security baseline.
④ "Validated input must be safe" — too much was delegated
The real question about an input is not "is this value correct" but "how much have I let this input decide". The more decisions you hand over, the less validation can keep up.
- Only the value
- Ordinary input. Checks on length, format and range work — the safe side
- The structure too
- Designs that accept a whole nested object and merge it. prototype pollution
- The type as well
- Formats that reconstruct objects. It is settled before your value checks run. insecure deserialization
- Where it lands
- Uploads that end up somewhere they can be executed. file upload vulnerabilities
- Who the request is
- Trusting a request header to decide the client's identity. X-Forwarded-For spoofing and trusted proxies
Counting the decisions you have handed over surfaces the dangerous places on its own. And the underlying cause is usually the same: trying to solve in validation what should be solved in structure — changing the shape you accept is more reliable than adding another check.
Doing it in order
Look at how production actually appears (①)
A URL that does not exist, a request that errors, the administrative paths — hit them over HTTP and look at what comes back. The point is to read the output, not the configuration. Headers can be measured in one go with the header scanner.
Write down everything you ever created (②)
Subdomains, accounts, keys, buckets, third-party services. The trick is not to filter by "still running". Build the list with the asset inventory checklist, and take unused entries all the way to deletion (not merely disabled).
Build exactly one path to noticing (③)
Trying to stand up complete monitoring usually results in none at all. Start with one — new-registration notifications, a spike in failed logins, or dependency vulnerability mail. One, including confirming it actually fires (monitoring that was configured but never verified is monitoring you do not have).
Count what your inputs are allowed to decide (④)
Write out whether external data decides only values, or also structure, type and destination. Anywhere it decides more than values is a high-priority place to change.
Make updates a mechanism (so ① does not come back)
The first shape reliably returns if left alone. Putting dependency and framework updates under machine monitoring is the only realistic answer (getting started with osv-scanner / a practical CVE remediation playbook). A procedure that lives in a document is the first thing to go on a busy day.
This site's view: all four were true once
What the four have in common is that each was correct at some point. Defaults were reasonable during development; unused things genuinely were unused; while you were small you really would have noticed; and input validation has solved a great many problems. That is exactly why they go unexamined.
Our position is to periodically ask when a formerly correct assumption stopped being correct. That check is cheaper and more effective than adding more technology. And note the difference in kind: a checklist tells you what to do, but not why something was missed — and if the reason is unchanged, the same gap reopens in the same place.
Read next
- As steps: the security baseline checklist (individuals and small teams) / the organisational security baseline
- Inventory: the asset inventory checklist
- From real cases: incident breakdowns (the same shapes keep reappearing)
- Trend: in 2026, attackers arrived with credentials more often than exploits
FAQ
QWhere should I start with security?
Checking which of four shapes your own environment matches works faster than learning individual attack techniques. Are production settings still at their defaults? Is anything still open that you created and never closed? Would you notice if something went wrong? How much are you letting external input decide? This article gives the concrete checks for each and links to the deeper articles.
QDoes this apply to a small personal site?
The shapes are the same; the ordering changes. For an individual or a small team, the two that pay off first are checking defaults (is debug output enabled in production?) and inventorying what was never closed (secrets in a public directory, unused subdomains, dormant accounts). Observability needs machinery so it can wait — but even a single signal, such as a notification on new user registrations, dramatically shortens how long a problem goes unnoticed.
QHow is this different from a checklist?
A checklist tells you what to do; this is about why things get missed. Listing items does not help if the reason they were missed is unchanged — the same gap reopens in the same place. All four beliefs were true once, which is exactly why they survive unexamined. When you want the actual steps, follow the links to the checklist articles from each section.
QI only have time for one. Which?
Close one gap in the third — being able to notice. The first two turn into incidents if ignored, but without the third you will not know an incident happened. Damage is decided less by the breach than by how long it runs unnoticed. Keep a log, add one notification, do one inventory a month: any of those is the single highest-return investment against an incident becoming a long one.