step 23 / 32 · 6 min
The handful of mistakes behind most breaches
Security is not a specialism you need before you can be careful. A small number of mistakes account for most real incidents, and avoiding them is habit rather than expertise.
Trust nothing that came from outside
Every value from a browser, an app or another service is attacker-controlled until you have checked it. That includes headers, cookies, file names and the hidden field somebody assumed nobody would edit.
The two classic failures both come from mixing data with instructions:
"SELECT * FROM users WHERE email = '" + input + "'"Given ' OR '1'='1, that returns every user. Parameterised queries fix it completely, because the value can never be read as SQL.
The same shape produces cross-site scripting when user text is written into a page as HTML, and the same fix applies: keep data as data.
Give the least access that works
The service that reads reports does not need permission to delete. Credentials scoped to exactly what is needed turn a break-in into a small break-in.
This is the argument against running as root in a container, and against one database user with full rights used by everything.
Never store what you do not need
Passwords are hashed with a slow algorithm designed for it, never encrypted and never stored as typed. Card numbers are best not stored at all. A secret in a config file in the repository is public the moment the repository is shared, which is why secrets live in the environment or a manager and are rotated when exposed.
Keep dependencies current
Most breaches are not clever. They are a known vulnerability in a library that had a fix available for months. Automated dependency updates are unglamorous and prevent more incidents than anything else on this page.
Fail closed
When the check cannot run, refuse. A permission system that allows the action because the permission service is down is worse than one that was never written, because everyone believes it is working.