The Secure by Default Mindset
Deny all, allow explicitly — the design principle that prevents most attacks before they start
Most security failures happen because the default is open and access is only restricted when someone remembers to do it. Secure by default inverts this: start with everything denied and explicitly allow what needs to be allowed. This lesson is a mindset shift, not a technical tutorial.
Deny all, allow explicitly
- Database: Row Level Security enabled by default — all access denied unless a policy exists — With Supabase RLS on, a table is completely inaccessible until you write a policy that explicitly grants access
- API: every endpoint requires authentication and authorisation unless explicitly marked public — Not "protect the sensitive endpoints" but "make every endpoint protected unless it is intentionally public"
- File system: minimum permissions for the process running your server — Your web server should only be able to read the files it needs — not write to system directories or read /etc/passwd
- Network: firewall default-deny — open only the ports your application uses — Port 443 (HTTPS) and 80 (HTTP redirect). Nothing else unless explicitly needed.
A nightclub with a guest list vs a nightclub with a banned list
A nightclub with a banned list lets everyone in by default and only blocks people on the list. Someone new — who should not be there — gets in until someone adds them to the list. A nightclub with a guest list only lets in people explicitly approved. A new face does not get in until someone approves them. Secure by default is the guest list model: deny everything, allow only what you have explicitly decided is safe.
The principle of least privilege
Every component — user accounts, API keys, service accounts, database users — should have the minimum permissions needed to do its job, and nothing more.
- Database user for the web app: SELECT, INSERT, UPDATE, DELETE on specific tables only — Not a root or superuser account. If the app is compromised, the attacker cannot DROP TABLE or access other databases.
- API keys: scoped to only the operations needed — A Stripe key for reading charges should not have write permissions. A Supabase key for one schema should not access another.
- Admin accounts: separate from regular user accounts, with MFA — Never use your admin account for day-to-day work. The more you use it, the more exposed it is.
Why defaults matter more than policies
Policies are followed when someone remembers to follow them. Defaults are enforced automatically, whether someone remembers or not. When you add a new endpoint, new table, or new user role, what happens by default? If the answer is "it is open until someone restricts it," you will eventually miss something.
Security debt compounds faster than technical debt
Adding security later is 5–10× more expensive than building it in. Every table added without RLS, every endpoint without auth, every variable hardcoded in a config file — these accumulate. A security-first approach prevents the accumulation.
Try this
For your current project, answer these three questions: (1) What happens when I add a new database table — is it publicly accessible by default? (2) What happens when I add a new API endpoint — does it require auth by default? (3) How many environment variables are checked into version control? The answers tell you your current security baseline.