Security is mostly won or lost in the small decisions you make while writing ordinary code: how you handle input, errors, secrets and dependencies. This lesson distills the daily habits that prevent the majority of vulnerabilities, independent of language or framework.
Every piece of external data, from users, APIs, files, headers or message queues, is untrusted until validated. Validate at the boundary of your system, as early as possible:
..%2F sneaks past your .. check.// Declarative input validation with Zod
const { z } = require('zod');
const CreateUser = z.object({
email: z.string().email().max(254),
name: z.string().min(1).max(100).regex(/^[\p{L} '-]+$/u),
age: z.number().int().min(13).max(120),
});
app.post('/users', (req, res) => {
const parsed = CreateUser.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: 'Invalid input' });
createUser(parsed.data); // only validated, typed data proceeds
res.status(201).end();
});Errors should never leave the system in a permissive state or leak internals. Return generic messages to clients, log details server-side, and make the default outcome of any failed check a denial. A try/catch that swallows an authorization error and continues is a vulnerability, not resilience.
API keys, database passwords and signing keys do not belong in source files or git history. Use environment variables at minimum, a secrets manager (Vault, AWS Secrets Manager, Doppler) for real systems. Add secret-scanning (gitleaks, GitHub secret scanning) to CI so an accidental commit is caught before it ships, and rotate any secret that ever leaks; deleting the commit is not enough.
Modern apps are mostly other people's code. Practices that keep that safe:
Whole vulnerability classes disappear when you choose the safe API shape:
execFile with an argument array instead of exec with a concatenated shell string.textContent and framework templating instead of innerHTML.Run processes as non-root users, scope API tokens to the minimum permissions and shortest lifetime, give the app's database account only the statements it needs, and separate duties between services. When something is compromised, least privilege is what decides between an incident and a catastrophe.
Next lesson: Network Security Basics: Firewalls, VPNs and Ports, zooming out from code to the network around it.