This checklist walks you through the practical steps to secure a REST API before it goes live. You’ll move from authentication and authorization to input validation, rate limiting, logging, and dependency hygiene. By the end, you’ll have a concrete list to verify against your own codebase, whether you’re building a new endpoint or auditing an existing one.
What You’ll Need
- Access to your API’s source code and deployment environment.
- A list of all endpoints, including HTTP methods and expected request/response formats.
- Your framework’s documentation (Express, Django, Spring, etc.) for middleware and security features.
- A tool like curl, Postman, or Insomnia for testing requests.
- If you use a cloud provider, access to its console or CLI for configuring security groups, WAFs, and secrets.
Step-by-Step
- Enforce HTTPS everywhere. Redirect all HTTP traffic to HTTPS at the load balancer or reverse proxy. Add HSTS headers (Strict-Transport-Security) to tell browsers to only use HTTPS. Disable TLS versions below 1.2 and configure a strong cipher suite. This protects data in transit from eavesdropping and man-in-the-middle attacks.
- Authenticate every request. Use a standard mechanism like OAuth 2.0 with short-lived access tokens, or API keys for server-to-server communication. Never accept credentials in query strings or logs. Store password hashes using a strong algorithm like bcrypt, scrypt, or Argon2 with a per-user salt. Implement token revocation for logout and compromise scenarios.
- Authorize on every endpoint. Don’t rely on hiding routes; check permissions on each request. Use role-based access control (RBAC) or attribute-based access control (ABAC) to ensure users can only access what they’re allowed to. For multi-tenant apps, verify that a user’s tenant ID matches the resource’s tenant ID to prevent cross-tenant data leaks.
- Validate all input. Whitelist expected fields, types, lengths, and patterns. Reject anything that doesn’t match. Use your framework’s validation library (e.g., Joi, Pydantic, Bean Validation) rather than writing ad-hoc checks. This blocks injection attacks (SQL, NoSQL, command) and malformed data that could crash your service.
- Sanitize output. Encode responses to prevent XSS if your API serves HTML or JavaScript. Set the
Content-Typeheader correctly (e.g.,application/json) and avoid returning raw user input without escaping. For JSON, useapplication/json; charset=utf-8and ensure your serialization library escapes HTML entities by default. - Implement rate limiting and throttling. Limit how many requests a client can make per time window (e.g., 100 requests/minute). Use a sliding window or token bucket algorithm. Return
429 Too Many Requestswith aRetry-Afterheader. This mitigates brute-force attacks, credential stuffing, and DDoS. - Set proper CORS policies. If your API is consumed by browsers, configure CORS to allow only specific origins. Avoid
Access-Control-Allow-Origin: *unless your API is intentionally public. Validate theOriginheader server-side and never reflect it without checking against an allowlist. - Use parameterized queries or an ORM. Never concatenate user input into SQL or NoSQL queries. Use prepared statements with bound parameters. If you use an ORM, be careful with raw queries and always pass parameters. This is the primary defense against SQL injection.
- Secure file uploads. If your API accepts files, validate the file type by magic bytes (not just extension), limit file size, and store files outside the web root. Use a random filename and serve them with a Content-Disposition header. Scan for malware if possible. Otherwise, treat uploads as untrusted and never execute them.
- Log everything relevant, but not secrets. Log request IDs, user IDs, timestamps, endpoint, method, status code, and response time. Never log passwords, tokens, API keys, or personal data. Use structured logging (JSON) to make searching easier. Ensure logs are immutable and access-controlled.
- Manage secrets properly. Store API keys, database passwords, and JWT secrets in environment variables or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault). Never hardcode secrets in source code or commit them to version control. Rotate secrets regularly and use different secrets per environment.
- Keep dependencies updated. Regularly run
npm audit,pip-audit, or your language’s equivalent to check for known vulnerabilities. Subscribe to security advisories for your framework and runtime. Update promptly, but test for breaking changes in a staging environment first. - Add security headers. In addition to HSTS, include
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: no-referrer, andContent-Security-Policywhere applicable. These headers protect against clickjacking, MIME sniffing, and other browser-based attacks. - Implement proper error handling. Return generic error messages to clients (e.g., “Invalid request”) and log detailed errors server-side. Avoid exposing stack traces, database queries, or internal paths in responses. Use a consistent error format like
{ "error": "message" }. - Test your security. Write automated tests for authentication, authorization, input validation, and rate limiting. Run a vulnerability scanner (e.g., OWASP ZAP) against a staging environment. Perform a manual penetration test focusing on OWASP Top 10 risks. Fix any findings before deployment.
- Set up monitoring and alerting. Monitor for unusual patterns like spikes in 401 errors, high request rates from a single IP, or failed validation attempts. Set up alerts for these anomalies. Use a tool like Prometheus + Grafana, or your cloud provider’s monitoring service.
Common Mistakes
- Relying on security through obscurity (e.g., unguessable URLs) instead of proper auth.
- Not validating the
Content-Typeheader, allowing JSON or XML injection. - Using JWT without an expiration time or not checking the issuer/audience.
- Storing secrets in environment files that are committed to Git.
- Disabling CORS for convenience during development and forgetting to enable it in production.
- Logging full request bodies that contain passwords or tokens.
- Not rate-limiting login or password reset endpoints, leaving them open to brute force.
Tips
- Use an API gateway (e.g., Kong, Tyk, or cloud-native) to centralize authentication, rate limiting, and caching.
- Implement idempotency keys for POST endpoints to prevent duplicate submissions.
- Use short-lived access tokens (e.g., 15 minutes) and refresh tokens with rotation.
- Run a dependency vulnerability scan in your CI/CD pipeline to catch issues early.
- Do a threat model for each new feature—think about who could attack it and how.
- Use a Web Application Firewall (WAF) in front of your API to filter common attacks.
Troubleshooting
- HTTPS not working: Check your certificate chain, make sure the private key matches the certificate, and verify that the load balancer is forwarding to port 443. Use
openssl s_client -connect yourdomain:443to test. - Rate limiter blocking legitimate users: Review your limits and consider using a token bucket that allows bursts. Add a
Retry-Afterheader and ensure your client code handles 429 responses gracefully. - CORS errors in browser: Verify the
Originheader sent by the browser and compare it to your allowlist. Check that your server responds with the correctAccess-Control-Allow-Originand handles preflight OPTIONS requests. - Tokens being rejected: Check that the token’s expiration time is in the future, the signature is valid, and the issuer/audience matches your configuration. Ensure the clock is synchronized between server and client.
- SQL injection still possible: Review your code for any place where you concatenate user input into a query string. Use an ORM’s parameterized methods consistently and avoid raw SQL unless absolutely necessary.
Frequently Asked Questions
Should I use JWT or session cookies?
JWT is stateless and works well for mobile and single-page apps. Session cookies are simpler for traditional server-rendered apps and allow easy revocation. Choose based on your architecture; if you use JWT, implement token revocation via a denylist or short expiry.
How do I handle API keys vs OAuth tokens?
API keys are for identifying a client (like a service account), not a user. OAuth tokens are for user authorization. Use API keys for server-to-server communication and OAuth for user-facing apps. Never use API keys for user authentication.
What is the best way to store passwords?
Use a slow hash function like bcrypt, scrypt, or Argon2 with a per-user salt. Never use MD5 or SHA-1. Set a high cost factor (e.g., bcrypt cost 12) and consider using a dedicated library like bcrypt or passlib.
How often should I rotate secrets?
Rotate secrets at least every 90 days, or immediately if you suspect a leak. Automate rotation using a secrets manager. Rotate database passwords and API keys more frequently if they are exposed to third parties.
Is it safe to expose my API to the public internet?
Yes, if you follow this checklist. Use a WAF, rate limiting, and strong auth. For internal APIs, consider a private network or VPN. Always assume the API is public and secure accordingly.
What should I do if my API is already compromised?
Immediately revoke all tokens and API keys, rotate database passwords, and review logs for unauthorized access. Patch the vulnerability that was exploited, then redeploy. Notify affected users if personal data was exposed, and consider a security audit.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Multi-Asset Trading Platforms: Why They’re Essential
- Creality K3 4-Nozzle 3D Printer: 4.8s Swap?
- Notepad Remove Duplicate Lines: 3 Simple Ways
- Storefront Hero Images: Desktop vs. Mobile Versions
- CentOS Kernel Upgrade: Step-by-Step Guide
- Device Encryption vs BitLocker: Key Differences