How to Protect Your API from Unauthorized Access

APIs handle 71% of all web traffic today, yet 78% of attacks succeed after authentication. If you’re relying solely on API keys or basic authentication, your API remains vulnerable to unauthorized access, data breaches, and service disruptions.
This article covers the essential security practices every developer needs: proper authentication methods (JWT, OAuth 2.0), authorization controls (RBAC), rate limiting, input validation, encryption, and API gateway implementation. You’ll learn how to build layered defenses that work together to prevent unauthorized access—even when attackers have valid credentials.
Key Takeaways
- Implement JWT or OAuth 2.0 for robust authentication beyond simple API keys
- Use Role-Based Access Control (RBAC) to manage permissions efficiently
- Apply rate limiting to prevent abuse and DoS attacks
- Validate all input data against strict schemas to block injection attacks
- Enforce HTTPS/TLS encryption for all API endpoints
- Deploy an API gateway for centralized security management
Authentication: Your First Line of Defense
JWT Authentication for Stateless Security
JSON Web Tokens (JWT) provide stateless authentication ideal for distributed systems and microservices. Unlike session-based auth, JWTs contain all necessary information within the token itself.
// Generate JWT with proper security claims
const jwt = require('jsonwebtoken');
function generateToken(user) {
return jwt.sign(
{
sub: user.id,
scope: user.permissions,
exp: Math.floor(Date.now() / 1000) + (15 * 60) // 15 minutes
},
process.env.JWT_SECRET,
{ algorithm: 'HS256' }
);
}
Critical JWT security practices:
- Set short expiration times (15-30 minutes)
- Use strong, randomly generated secrets
- Validate algorithm explicitly to prevent algorithm confusion attacks
- Implement refresh token rotation for long-lived sessions
OAuth 2.0 for Third-Party Access
OAuth 2.0 excels when you need delegated authorization—allowing third-party applications to access your API without sharing credentials. Use OAuth 2.0 with PKCE (Proof Key for Code Exchange) extension for mobile apps and SPAs to prevent authorization code interception.
Authorization: Controlling What Users Can Do
Implement Role-Based Access Control (RBAC)
Authentication verifies identity; authorization determines permissions. RBAC assigns permissions to roles rather than individual users, simplifying access management.
const permissions = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read']
};
function authorize(requiredPermission) {
return (req, res, next) => {
const userPermissions = permissions[req.user.role];
if (!userPermissions?.includes(requiredPermission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
Apply the principle of least privilege—grant only the minimum access necessary for each role.
Discover how at OpenReplay.com.
Rate Limiting: Preventing Abuse and DoS Attacks
Rate limiting protects against brute force attacks, DoS attempts, and resource exhaustion. Implement tiered limits based on user roles or subscription levels.
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100, // requests per window
standardHeaders: true, // Return rate limit info in headers
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retryAfter: req.rateLimit.resetTime
});
}
});
For distributed systems, use Redis to share rate limit counters across instances.
Input Validation: Stopping Injection Attacks
Never trust client input. Validate all incoming data against strict schemas to prevent SQL injection, XSS, and command injection attacks.
const Ajv = require('ajv');
const ajv = new Ajv();
const userSchema = {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 18, maximum: 120 }
},
required: ['email'],
additionalProperties: false // Prevent mass assignment
};
const validate = ajv.compile(userSchema);
if (!validate(req.body)) {
return res.status(400).json({ errors: validate.errors });
}
Encryption: Protecting Data in Transit
Always Use HTTPS/TLS
Enforce HTTPS for all API endpoints—no exceptions. Use TLS 1.3 when possible and implement HTTP Strict Transport Security (HSTS) headers to prevent downgrade attacks.
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
next();
});
API Gateway Security: Centralized Protection
An API gateway like Kong, AWS API Gateway, or Tyk provides a single control point for security policies. Gateways handle:
- Authentication and authorization
- Rate limiting and throttling
- Request/response transformation
- Logging and monitoring
- DDoS protection
This centralized approach simplifies security management and ensures consistent policy enforcement across all endpoints.
Common Security Pitfalls to Avoid
Never store secrets in frontend code. API keys, tokens, and credentials in client-side JavaScript are visible to anyone who inspects the code.
Don’t rely solely on CORS for security. CORS prevents browsers from making unauthorized requests but doesn’t protect against direct API calls from tools like Postman or curl.
Avoid exposing internal services directly. Always route external traffic through an API gateway or reverse proxy that enforces security policies.
Don’t use long-lived tokens without rotation. Implement token refresh mechanisms and invalidate tokens on logout or suspicious activity.
Building Layered API Security
No single security measure provides complete protection. Effective API security requires multiple layers working together:
- Authentication verifies identity
- Authorization controls access
- Rate limiting prevents abuse
- Input validation blocks malicious data
- Encryption protects data in transit
- API gateways centralize security controls
Each layer compensates for potential weaknesses in others. When an attacker bypasses one defense, the next layer stops them.
Conclusion
Protecting your API from unauthorized access requires more than just adding authentication. By implementing JWT or OAuth 2.0 authentication, RBAC authorization, rate limiting, input validation, HTTPS encryption, and API gateway security, you create a robust defense system that protects against both external threats and insider risks.
Start with the basics—HTTPS, authentication, and rate limiting—then progressively add layers based on your risk profile. Remember: security isn’t a one-time implementation but an ongoing process that evolves with your API and the threat landscape.
FAQs
Authentication verifies who a user is by checking credentials like passwords or tokens. Authorization determines what that authenticated user can do by checking their permissions. Both are essential but serve different purposes in API security.
JWT tokens should expire between 15 to 30 minutes for high-security applications. Shorter expiration times limit damage if tokens are compromised. Use refresh tokens that last longer but can be revoked to maintain user sessions without frequent re-authentication.
Yes, overly restrictive rate limits can block legitimate users during traffic spikes. Implement tiered limits based on user roles or subscription levels. Monitor usage patterns to set appropriate thresholds and provide clear error messages with retry information when limits are reached.
No, HTTPS only encrypts data in transit between client and server. You still need authentication, authorization, input validation, and rate limiting. HTTPS is essential but represents just one layer in a comprehensive API security strategy.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.