In 2024 alone, API-related breaches exposed over 5 billion records globally. The uncomfortable truth? Most of these exploited vulnerabilities that are well-documented and entirely preventable. Let's walk through the OWASP API Security Top 10 with practical Node.js examples.
1. Broken Object-Level Authorization (BOLA)
The #1 API vulnerability. It happens when your API lets users access objects they shouldn't by simply changing an ID in the URL.
// BAD: No ownership check
app.get('/api/orders/:id', async (req, res) => {
const order = await Order.findById(req.params.id);
res.json(order); // Anyone can see any order!
});
// GOOD: Verify ownership
app.get('/api/orders/:id', async (req, res) => {
const order = await Order.findOne({
_id: req.params.id,
userId: req.user.id // Only return if owned by the user
});
if (!order) return res.status(404).json({ error: 'Not found' });
res.json(order);
});
2. Broken Authentication
Use proven libraries (Passport.js, next-auth). Never roll your own JWT verification. Always:
- Set short token expiry (15 min for access tokens)
- Use refresh token rotation
- Implement rate limiting on auth endpoints
- Hash passwords with bcrypt (cost factor 12+)
3. Excessive Data Exposure
Never return full database objects. Use DTOs or explicit field selection:
// BAD: Returns password hash, internal IDs, etc.
res.json(user);
// GOOD: Explicit allowlist
res.json({
id: user.id,
name: user.name,
email: user.email,
});
4. Rate Limiting & Resource Protection
Use express-rate-limit with different tiers: strict for auth endpoints (5 req/min), moderate for mutations (30 req/min), relaxed for reads (100 req/min). Add request size limits and query complexity analysis for GraphQL.
5. Input Validation at Every Boundary
Use Zod for runtime validation. Every API endpoint should validate its input before processing:
import { z } from 'zod';
const CreateOrderSchema = z.object({
productId: z.string().uuid(),
quantity: z.number().int().min(1).max(100),
notes: z.string().max(500).optional(),
});
// In your handler
const result = CreateOrderSchema.safeParse(req.body);
if (!result.success) return res.status(400).json(result.error);
The Security Mindset
Security isn't a feature you add at the end — it's a practice you build into every line of code. At Kougar Logic, security reviews are part of every PR, not an afterthought.
Need a security audit for your APIs? Our team can help.