REST API Security Best Practices

advanced
15 min

REST API Security Best Practices

APIs are the front door to your data, and attackers know it: APIs are now the most common attack vector for web applications. Everything in this course so far, auth, validation, error design, feeds into security. This final lesson organizes it into a practical hardening checklist, aligned with the recurring themes of the OWASP API Security Top 10.

Transport: HTTPS Everywhere, No Exceptions

Every API endpoint must require TLS. Serve nothing meaningful over plain HTTP; redirect or reject it, and set HSTS (Strict-Transport-Security) so clients never downgrade. Tokens, API keys, and personal data on an unencrypted channel are public data. Internally, service-to-service traffic deserves TLS too; the network is not trustworthy just because you own it.

Object-Level Authorization: The Number One API Bug

The most common real-world API vulnerability is Broken Object Level Authorization (BOLA): the server checks that a caller is logged in, but not that the object belongs to them. An attacker who owns invoice 1041 simply requests /invoices/1042.

javascript
// Vulnerable: any authenticated user can read any invoice app.get('/invoices/:id', requireAuth, async (req, res) => { res.json(await invoices.findById(req.params.id)); }); // Fixed: ownership is part of the query itself app.get('/invoices/:id', requireAuth, async (req, res) => { const inv = await invoices.findOne({ id: req.params.id, ownerId: req.user.sub }); if (!inv) return res.status(404).json({ error: 'not found' }); res.json(inv); });

Check authorization on every object access, and return 404 rather than 403 for objects outside the caller's scope so you do not confirm their existence. Using non-guessable IDs (UUIDs) helps, but is never a substitute for the check.

Input: Validate, Parameterize, Allow-List

All injection defenses reduce to one principle: never let untrusted input become code. Use parameterized queries or an ORM so SQL injection is structurally impossible; never concatenate query strings. Validate every body, query, and path parameter against a schema, rejecting unknown fields, as covered in the previous lesson. That strict validation also blocks mass assignment attacks, where a caller adds "role": "admin" to a profile update and a careless handler persists it; explicitly allow-list the fields each endpoint may write.

Rate Limiting and Resource Caps

Without limits, one script can brute-force logins, enumerate resources, or simply bankrupt your infrastructure. Rate limit per key or user (not just per IP), with tighter buckets on sensitive endpoints like login and password reset. Return 429 with Retry-After and expose X-RateLimit-* headers. Cap costs elsewhere too: maximum page sizes, maximum request body size (express.json({ limit: '100kb' })), and timeouts on all upstream calls.

Don't Leak Information

Every leak compounds other attacks. Return generic 500 messages while logging details privately. Keep secrets in environment variables or a secrets manager, never in code or version control. Disable framework fingerprints like the X-Powered-By header, and use helmet in Express to set a battery of protective headers in one line. Configure CORS with an explicit allow-list of origins; a reflected wildcard with credentials is equivalent to no CORS at all. Finally, make error and 404 behavior uniform so attackers cannot map your internal structure from response differences.

Operate Securely

Security is ongoing, not a launch checklist. Log authentication events, authorization denials, 4xx and 429 spikes, and alert on anomalies; a credential-stuffing attack looks like a spike of 401s. Keep dependencies patched (npm audit in CI), rotate credentials on a schedule, and test your API the way an attacker would: request other users' objects, replay expired tokens, send oversized and malformed bodies. Once a year, hire someone whose job is to break it.

Key Takeaways

  • HTTPS with HSTS everywhere; secrets never touch code, URLs, or logs.
  • Enforce object-level authorization on every access; 404 what callers do not own.
  • Schema validation, parameterized queries, and field allow-lists neutralize injection and mass assignment.
  • Rate limit per identity, cap payloads and page sizes, and monitor auth failures continuously.

Congratulations: you have completed the REST APIs course on codeyourcraft.com. Revisit Designing a Complete CRUD API and build one end to end with everything you now know.

REST API Security Best Practices - REST APIs | CodeYourCraft | CodeYourCraft