Home / Courses / Security for Beginners / III — Best Practices
III — Best Practices
Article 8 of 8

Best Practices & Next Steps

A practical API security checklist, common mistakes to avoid, and a roadmap for what to learn next after mastering authentication, authorization, anti-injection, and secrets management.

July 10, 2026

Congratulations — you've built task-tracker-api with a complete set of security layers: authentication with password hashing and JWT, per-resource authorization, rate limiting, injection protection, correct secrets management, security headers, and scanned dependencies. In this closing article, we'll round it all up into a practical checklist, common mistakes, and a roadmap for what's next.

API Security Checklist

Use this list as a quick review before calling a new feature "done":

  • [ ] Passwords hashed with bcrypt (or argon2), never stored as plaintext (Part 3)
  • [ ] Tokens/sessions have a reasonable expiration (Part 3)
  • [ ] Login error messages are generic, don't leak which usernames are valid (Part 3)
  • [ ] EVERY endpoint accessing a specific resource verifies ownership/permission, not just authentication (Part 4)
  • [ ] Sensitive endpoints (login, register) have rate limiting (Part 4)
  • [ ] All database access goes through an ORM/parameterized query, no manual SQL strings from user input (Part 5)
  • [ ] An explicit response model on every endpoint, never exposing a raw database object (Part 3, 5)
  • [ ] Every input has validation constraints (length, type, format) (Part 5)
  • [ ] No secret hardcoded in code or committed to git (Part 6)
  • [ ] CORS allows specific origins, not a * wildcard (Part 6)
  • [ ] Basic security headers are attached (X-Content-Type-Options, X-Frame-Options, etc.) (Part 6)
  • [ ] Dependencies are scanned regularly (pip-audit or equivalent) (Part 7)
  • [ ] The container doesn't run as root (Part 7)
  • Common Mistakes

  • Treating authentication as equal to authorization. This is the most common and most dangerous mistake (BOLA, Parts 2 & 4) — knowing WHO a user is isn't the same as knowing WHAT they're allowed to access. Every endpoint touching a specific resource needs its own ownership check.
  • Validating only on the frontend. The frontend can be bypassed entirely — anyone can call the API directly via curl or Postman, never touching the frontend at all. Server-side validation (Part 5) is MANDATORY; frontend validation is only for a better user experience.
  • Trusting "it's just an internal tool/prototype anyway." Plenty of "temporary" prototypes end up in production without ever getting a security review. Building good security habits from the start is far cheaper than retrofitting them later.
  • Storing a JWT in localStorage without thinking it through. A JWT stored in a browser's localStorage is vulnerable to theft via XSS (if the app has an XSS gap). For applications needing higher security, consider an httpOnly cookie that JavaScript can't access at all.
  • A rate limit that's easy to bypass. An IP-based rate limit alone (like the one we built in Part 4) can be bypassed by rotating IPs (via a proxy/VPN). For stronger protection, combine it with an account/username-based rate limit, and consider a CAPTCHA for suspicious cases.
  • Never testing "what happens if this gets attacked." Just like we proved SQL injection fails in Part 5 and BOLA is prevented in Part 4 — the habit of actually trying to "attack" your own feature before calling it done is far more effective than assuming.
  • Roadmap — What's Next

    This series focused on the fundamentals that most often become real gaps. If you want to go further:

  • Refresh tokens & token revocation — our JWT access token is valid until it expires and can't be "revoked" early (say, when a user logs out or a device is stolen). The refresh-token pattern (a short-lived access token + a longer-lived, revocable refresh token) solves this.
  • Multi-Factor Authentication (MFA/2FA) — a second verification layer (a code from an authenticator app, SMS, or email) on top of a password, making an account far harder to break into even if the password leaks.
  • OAuth2 / Single Sign-On (SSO) — allowing login via Google/GitHub/etc., shifting the responsibility of storing credentials to an already-proven provider instead of storing them ourselves.
  • Automated security scanning in CI/CD — adding pip-audit, image scanning (Trivy, Docker Scout), and static application security testing (Bandit for Python) as automated steps in the pipeline, following the pattern already covered in CI/CD for Beginners.
  • A Web Application Firewall (WAF) — a protective layer in front of the application that filters common attack patterns (SQL injection, XSS payloads, bot traffic) before they ever reach the server.
  • Audit logging — recording WHO did WHAT and WHEN for sensitive actions (login, changes to important data), separate from regular operational logs — crucial for investigating an incident.
  • Regular penetration testing & security audits — inviting a party (internal or external) to actively try to break into the system in a controlled way, finding gaps before a real attacker does.
  • Secrets rotation — regularly rotating the secret key, database credentials, and API keys (and immediately after any sign of a leak), instead of using them forever from the moment they were first created.
  • How This Connects to Other Series

    This series continues task-tracker-api from FastAPI for Beginners and Scalability for Beginners. Once your API is secure AND scalable, CI/CD for Beginners closes the loop — automating tests (including the security tests we just wrote) and deployment, so every change is validated consistently before reaching production.

    Closing Thoughts

    We've made the journey from "why security matters," through understanding common threats via the OWASP framework, all the way to building concrete defenses layer by layer — authentication, authorization, anti-injection, secrets management, and dependency scanning. Most importantly, every security claim in this series was proven with a real attempt: we actually tried SQL injection and it failed, we actually tested two users trying to peek at each other's data and it was prevented, we actually triggered the rate limit and got blocked. Security isn't a checklist you tick off and forget — it's the habit of asking "how could this be abused?" on every new feature you build.

    Thanks for following along with this Security for Beginners series. Good luck securing your own systems!

    Topics

    SecurityBest Practices
    III — Best Practices · Article 8 of 8