{"componentChunkName":"component---src-components-blog-template-js","path":"/blog/2025-01-12-api-security/","result":{"data":{"markdownRemark":{"frontmatter":{"title":"API Security","date":"2025-01-12"},"html":"<p>APIs are often the most exposed part of an application. They handle authentication, serve data, and accept user input. If an API is not properly secured, it becomes an easy target. There are several layers of protection we should apply.</p>\n<h3>Authentication and Rate Limiting</h3>\n<p>Every API endpoint that handles sensitive data should require authentication. Tokens, typically JWTs, are the standard approach for stateless authentication. The token is included in the <code class=\"language-text\">Authorization</code> header of each request, and the server validates it before processing.</p>\n<p>Rate limiting prevents abuse by capping the number of requests a client can make within a time window. Without it, an attacker can hammer the login endpoint with thousands of password attempts or overwhelm the server with traffic. A common setup is to allow a certain number of requests per minute per IP address or per authenticated user.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">// Express rate limiting example\nconst rateLimit = require(\"express-rate-limit\");\n\nconst limiter = rateLimit({\n  windowMs: 15 * 60 * 1000,\n  max: 100,\n});\n\napp.use(\"/api/\", limiter);</code></pre></div>\n<h3>Input Validation</h3>\n<p>Never trust data coming from the client. Every piece of input should be validated and sanitised on the server, regardless of whether the frontend already validates it. SQL injection, cross-site scripting (XSS), and other injection attacks exploit APIs that pass user input directly to databases or templates without proper validation.</p>\n<p>Using a validation library like Zod or Joi ensures that incoming data matches the expected schema before it reaches the business logic.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">const schema = z.object({\n  email: z.string().email(),\n  name: z.string().min(1).max(100),\n  age: z.number().int().positive().optional(),\n});</code></pre></div>\n<p>If the data does not match, the request is rejected with a clear error before anything else happens.</p>\n<h3>CORS</h3>\n<p>Cross-Origin Resource Sharing (CORS) controls which domains can make requests to our API. By default, browsers block requests from a different origin than the one serving the page. We configure CORS on the server to specify which origins are allowed.</p>\n<p>A common mistake is setting CORS to allow all origins (<code class=\"language-text\">*</code>) in production. This should be restricted to only the domains that legitimately need to access the API.</p>\n<h3>HTTPS and Headers</h3>\n<p>All API traffic should go over HTTPS. Without it, data including tokens and credentials travels in plain text and can be intercepted. Most hosting platforms handle TLS certificates automatically now, so there is little reason not to use HTTPS.</p>\n<p>Security headers like <code class=\"language-text\">Strict-Transport-Security</code>, <code class=\"language-text\">X-Content-Type-Options</code>, and <code class=\"language-text\">X-Frame-Options</code> add additional protection. Libraries like Helmet for Express set these headers with sensible defaults.</p>\n<h3>Least Privilege</h3>\n<p>API endpoints should only return the data that the client needs. Exposing internal IDs, user emails, or system details in API responses gives attackers more information to work with. Similarly, each API key or service account should have the minimum permissions required for its function. If a service only needs to read from the database, its credentials should not allow writes.</p>\n<h3>Logging and Alerting</h3>\n<p>Logging authentication failures, unusual access patterns, and validation errors helps detect attacks early. If there is a sudden spike in failed login attempts from a single IP, an alert can trigger an investigation before real damage is done.</p>"}},"pageContext":{"slug":"/2025-01-12-api-security/"}},"staticQueryHashes":[]}