APIs are the backbone of modern applications, but they’re also a prime target for attackers. Here’s how to secure your APIs effectively.
Why API Security Matters
APIs expose your application’s functionality and data to the world. A single security flaw can lead to:
- Data breaches and unauthorized access
- Service disruption and downtime
- Compliance violations and fines
- Reputation damage
Essential API Security Practices
1. Authentication and Authorization
Always authenticate API requests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // Example: JWT authentication middleware
const authenticateToken = (req, res, next) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
};
|
Implement proper authorization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // Check user permissions
const requirePermission = (permission) => {
return (req, res, next) => {
if (!req.user.permissions.includes(permission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
app.delete('/api/users/:id',
authenticateToken,
requirePermission('delete:users'),
deleteUser
);
|
2. Rate Limiting
Prevent abuse and DDoS attacks with rate limiting:
1
2
3
4
5
6
7
8
9
| const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests, please try again later.'
});
app.use('/api/', limiter);
|
Always validate and sanitize input:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| const { body, validationResult } = require('express-validator');
app.post('/api/users',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').trim().escape(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process request
}
);
|
4. HTTPS Only
Never transmit sensitive data over HTTP:
1
2
3
4
5
6
7
8
| // Redirect HTTP to HTTPS
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});
|
5. API Versioning
Maintain backward compatibility and security:
1
2
3
4
5
6
7
8
9
10
| // Version in URL
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
// Or in headers
app.use((req, res, next) => {
const version = req.headers['api-version'] || '1';
req.apiVersion = version;
next();
});
|
Advanced Security Measures
1. API Gateway
Use an API gateway for centralized security:
- Authentication and authorization
- Rate limiting and throttling
- Request/response transformation
- Logging and monitoring
2. OAuth 2.0 and OpenID Connect
Implement industry-standard authentication:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // OAuth 2.0 authorization code flow
app.get('/auth/callback', async (req, res) => {
const { code } = req.query;
const tokenResponse = await fetch('https://oauth.provider.com/token', {
method: 'POST',
body: JSON.stringify({
grant_type: 'authorization_code',
code,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URI
})
});
const { access_token } = await tokenResponse.json();
// Store token securely
});
|
3. API Keys Management
Secure API key handling:
- Never expose keys in client-side code
- Rotate keys regularly
- Use different keys for different environments
- Implement key expiration
- Monitor key usage
4. CORS Configuration
Configure CORS properly:
1
2
3
4
5
6
7
8
9
| const cors = require('cors');
app.use(cors({
origin: process.env.ALLOWED_ORIGINS.split(','),
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // 24 hours
}));
|
Implement security headers:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
|
Logging and Monitoring
Track API usage and security events:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // Log all API requests
app.use((req, res, next) => {
logger.info({
method: req.method,
path: req.path,
ip: req.ip,
user: req.user?.id,
timestamp: new Date().toISOString()
});
next();
});
// Alert on suspicious activity
if (failedAttempts > 5) {
alertSecurityTeam({
type: 'brute_force_attempt',
ip: req.ip,
endpoint: req.path
});
}
|
Testing API Security
Regular security testing is crucial:
- Automated Scanning: Use tools like WebSecurityScore
- Penetration Testing: Conduct regular pen tests
- Fuzzing: Test with unexpected inputs
- Load Testing: Verify rate limiting works
Common API Vulnerabilities
1. Broken Object Level Authorization (BOLA)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Vulnerable
app.get('/api/users/:id', (req, res) => {
const user = db.getUser(req.params.id);
res.json(user); // Returns any user!
});
// Secure
app.get('/api/users/:id', authenticateToken, (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const user = db.getUser(req.params.id);
res.json(user);
});
|
2. Mass Assignment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // Vulnerable
app.put('/api/users/:id', (req, res) => {
db.updateUser(req.params.id, req.body); // Can update any field!
});
// Secure
app.put('/api/users/:id', (req, res) => {
const allowedFields = ['name', 'email', 'bio'];
const updates = {};
allowedFields.forEach(field => {
if (req.body[field] !== undefined) {
updates[field] = req.body[field];
}
});
db.updateUser(req.params.id, updates);
});
|
Conclusion
API security requires a multi-layered approach combining authentication, authorization, input validation, rate limiting, and continuous monitoring. Regular security testing and staying updated on emerging threats are essential.
Secure your APIs with automated security testing. Start scanning your APIs today with WebSecurityScore.