Why this matters
Backend engineers routinely handle user data, credentials, sessions, and internal services. One missed control can leak data, allow account takeover, or disrupt operations. You will use these fundamentals when you:
- Design login and session flows (e.g., cookie flags, token TTLs, refresh rotation)
- Build data access layers (parameterized queries, least-privilege DB users)
- Expose APIs (rate limits, input validation, output encoding, CORS)
- Handle secrets (keys, tokens, passwords) safely
- Harden deployments (TLS, security headers, environment separation)
Concept explained simply
Security is preventing bad outcomes while still letting good users do their work. You protect data, verify identities, restrict actions, and minimize damage if something goes wrong.
Mental model
- Think layers: prevent, detect, limit blast radius, recover (defense-in-depth).
- Assume compromise: plan for least privilege, short-lived access, and logs to investigate.
- Secure defaults: safest behavior should require no extra configuration.
Core principles (quick checklist)
Key topics, briefly
- AuthN vs AuthZ: Authentication proves identity; Authorization checks what that identity can do.
- Password storage: Use a slow, salted hash (Argon2id or bcrypt), never plain or reversible.
- Sessions and tokens: Prefer HttpOnly+Secure cookies; set SameSite; keep access tokens short-lived; rotate refresh tokens.
- Injections: Use parameterized queries and safe ORM methods.
- CSRF: Protect state-changing endpoints when using cookie-based auth (CSRF tokens + SameSite cookies).
- Rate limiting and lockouts: Throttle brute force and abusive patterns.
- Security headers: HSTS, Content-Security-Policy, X-Content-Type-Options, and cookie flags.
- Transport security: Enforce HTTPS end-to-end; redirect HTTP to HTTPS; use modern TLS.
- Secrets: Store in a secret manager or env vars provided securely by your platform; rotate and scope.
- Environment separation: Isolate dev/test/prod; never use prod secrets in non-prod.
Worked examples
1) Stop SQL injection with parameterized queries
Vulnerable:
// Pseudocode
const user = req.query.user; // ?user=alice' OR '1'='1
const sql = "SELECT * FROM accounts WHERE username = '" + user + "'";
db.query(sql);
Safe (parameterized):
// Example with placeholders
const sql = "SELECT * FROM accounts WHERE username = ?";
db.query(sql, [req.query.user]);
- Never concatenate untrusted input into SQL.
- Use prepared statements or a safe ORM.
2) Store passwords securely with a slow hash
// Bad
save({ username, password }); // plaintext
// Good (pseudocode)
const hash = argon2id.hash(password, { memoryCost: high, timeCost: reasonable, parallelism: 1 });
save({ username, password_hash: hash });
// Verify
const ok = argon2id.verify(storedHash, inputPassword);
- Use Argon2id or bcrypt with strong parameters.
- Each user gets a unique salt; libraries handle this for you.
- Never log or email passwords.
3) Safer sessions with cookies and short-lived tokens
// Set access token cookie (short TTL) and refresh token cookie (rotated)
Set-Cookie: access=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=900
Set-Cookie: refresh=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/auth/refresh; Max-Age=2592000
- HttpOnly prevents JS access, reducing XSS impact.
- SameSite=Strict/Lax reduces CSRF risk (still use CSRF tokens for sensitive actions).
- Rotate refresh tokens on every use; revoke on suspicion.
4) Basic rate limiting to reduce abuse
// Pseudocode: allow 5 login attempts per 5 minutes per IP or account
if (attempts(username, ip) > 5 in 5m) {
return 429; // Too Many Requests
}
- Combine per-IP and per-account limits.
- Return generic errors to avoid username enumeration.
Exercises
Do these to internalize the concepts. The quick test at the end is available to everyone; log in to save your progress.
Exercise 1: Patch the SQL injection
See the Exercises panel below for full instructions. Goal: convert a vulnerable query into a parameterized one and restrict DB permissions to least privilege.
Exercise 2: Secure your session
Move tokens out of JavaScript-accessible storage into HttpOnly+Secure cookies. Add sensible expirations and refresh rotation.
Exercise 3: Add essential security headers
Return key headers (HSTS, CSP, X-Content-Type-Options) and set cookie flags.
Common mistakes and self-check
- Mistake: Storing tokens in localStorage. Fix: Use HttpOnly cookies with Secure and SameSite.
- Mistake: Fast hashes (SHA-256) for passwords. Fix: Use Argon2id or bcrypt with strong parameters.
- Mistake: Trusting client-side validation. Fix: Always validate on the server.
- Mistake: Building SQL strings via concatenation. Fix: Parameterized queries only.
- Mistake: Logging secrets/PII. Fix: Mask/redact sensitive fields; restrict log access.
- Mistake: Long-lived access tokens. Fix: Short TTLs and refresh rotation; revoke on logout/compromise.
Self-check tips:
- Try known-bad inputs (quotes, semicolons, HTML/JS). Did your app behave safely?
- Open DevTools > Network. Do responses include HSTS, CSP, and proper cookie flags?
- Temporarily block TLS (use http). Does your app redirect to https and enforce HSTS?
Practical projects
- Build a tiny auth service with signup/login, bcrypt/Argon2id, HttpOnly cookies, and refresh rotation.
- Wrap your database access in a safe query module that only exposes parameterized calls.
- Create a middleware that sets security headers and rate limits per route.
Who this is for
- Backend beginners who need safe defaults fast.
- Engineers moving from prototypes to production-readiness.
- Anyone reviewing APIs, auth flows, or data-access layers.
Prerequisites
- Basic server programming (routing, handlers, middleware).
- Familiarity with HTTP, cookies, and a relational or document database.
Learning path
- Now: Security fundamentals (this page).
- Next: API design and validation, secure data access, observability and incident response.
- Later: Advanced topics like OAuth2/OIDC, key management, and threat modeling.
Mini challenge
Harden a single endpoint end-to-end. Pick your most sensitive POST route and ensure all of these:
- AuthZ check (RBAC/ABAC) enforced server-side
- Input schema validation with safe defaults
- Idempotency protection if applicable
- CSRF defense (if cookie-based auth)
- Security headers present in response
- Structured log with no secrets/PII
Next steps
- Complete the exercises and take the quick test below.
- Add these checks to your project template so every new service starts secure-by-default.
- Schedule periodic reviews for secrets, dependencies, and headers.
Quick test
The quick test is available to everyone. Log in to save your score and track progress across lessons.