The API Security Landscape in 2026: Top Threats
The API Security Landscape in 2026: Top Threats
APIs are the #1 attack vector for web applications. They expose business logic directly, handle sensitive data, and scale to millions of requests. In 2026, API attacks are more sophisticated — automated bots probe for BOLA vulnerabilities, AI generates payload variations, and the attack surface grows with every new endpoint.
The Numbers
| Metric | Value |
|---|---|
| API attacks increased (2024→2025) | +40% |
| APIs involved in data breaches | ~60% |
| Average cost of API breach | $4.5M |
| APIs with at least one vulnerability | ~70% |
| Time to detect API breach (avg) | 200+ days |
| Shadow APIs in enterprise environments | 30-50% of all APIs |
OWASP API Security Top 10 (2023, Still Relevant in 2026)
1. Broken Object Level Authorization (BOLA)
The #1 API vulnerability. Users can access other users' data by changing an ID.
// ❌ Vulnerable — no ownership check
app.get('/api/orders/:orderId', async (req, res) => {
const order = await db.orders.findById(req.params.orderId);
return res.json(order);
// Any authenticated user can read ANY order by guessing IDs
});
// ✅ Fixed — verify ownership
app.get('/api/orders/:orderId', async (req, res) => {
const order = await db.orders.findById(req.params.orderId);
if (!order) return res.status(404).json({ error: 'Not found' });
if (order.userId !== req.user.id) {
return res.status(403).json({ error: 'Forbidden' });
}
return res.json(order);
});
Prevention:
- Always check resource ownership, not just authentication
- Use UUIDs instead of sequential IDs (harder to enumerate)
- Implement authorization middleware that runs on every request
- Test with horizontal privilege escalation checks
2. Broken Authentication
Weak authentication lets attackers impersonate users or bypass auth entirely.
// Common authentication mistakes:
// ❌ No rate limiting on login
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
// Attacker can brute-force unlimited attempts
});
// ❌ JWT with no expiration
const token = jwt.sign({ userId: user.id }, secret);
// Token valid forever — if leaked, permanent access
// ❌ API key in URL
GET /api/data?api_key=sk_live_abc123
// Logged in access logs, browser history, referrer headers
// ✅ Secure auth pattern
app.post('/api/login', rateLimit({ max: 5, window: '15m' }), async (req, res) => {
const { email, password } = req.body;
const user = await db.users.findByEmail(email);
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
return res.status(401).json({ error: 'Invalid credentials' });
// Same error for wrong email AND wrong password (prevents enumeration)
}
const token = jwt.sign(
{ userId: user.id, role: user.role },
secret,
{ expiresIn: '1h' } // Short-lived token
);
const refreshToken = generateRefreshToken(user.id);
// Store refresh token securely, set httpOnly cookie
});
3. Broken Object Property Level Authorization
APIs return more data than they should, or accept writes to fields they shouldn't.
// ❌ Over-exposing data
app.get('/api/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
return res.json(user);
// Returns: { id, name, email, passwordHash, ssn, role, isAdmin, internalNotes }
});
// ❌ Mass assignment
app.put('/api/users/:id', async (req, res) => {
await db.users.update(req.params.id, req.body);
// Attacker sends: { "role": "admin", "isAdmin": true }
});
// ✅ Explicit field selection
app.get('/api/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id, {
select: ['id', 'name', 'email', 'avatar'],
});
return res.json(user);
});
// ✅ Allowlist writable fields
app.put('/api/users/:id', async (req, res) => {
const allowed = ['name', 'email', 'avatar'];
const updates = Object.fromEntries(
Object.entries(req.body).filter(([key]) => allowed.includes(key))
);
await db.users.update(req.params.id, updates);
});
4. Unrestricted Resource Consumption
No limits on request size, frequency, or complexity.
// ❌ No limits
app.post('/api/search', async (req, res) => {
const results = await db.products.find(req.body.query);
// Attacker sends complex query that scans entire database
// Or sends 10,000 requests/second
});
// ✅ Multiple layers of protection
app.post('/api/search',
rateLimit({ max: 100, window: '1m' }), // Rate limit
validateBody({ query: z.string().max(500) }), // Input size limit
async (req, res) => {
const results = await db.products.find(req.body.query, {
limit: 50, // Result limit
timeout: 5000, // Query timeout
});
return res.json(results);
}
);
5. Broken Function Level Authorization
Users can access admin endpoints by simply calling them.
// ❌ No role check
app.delete('/api/admin/users/:id', async (req, res) => {
await db.users.delete(req.params.id);
// Any authenticated user can delete any user
});
// ✅ Role-based middleware
function requireRole(...roles: string[]) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.delete('/api/admin/users/:id',
requireAuth,
requireRole('admin'),
async (req, res) => {
await db.users.delete(req.params.id);
}
);
New Threats in 2026
AI-Powered Attacks
| Attack Type | How AI Helps Attackers |
|---|---|
| Automated BOLA scanning | AI systematically probes ID patterns across endpoints |
| Intelligent fuzzing | LLMs generate context-aware payloads that bypass WAFs |
| API schema inference | AI maps undocumented endpoints from error messages |
| Credential stuffing | AI rotates patterns to avoid rate limiting detection |
| Social engineering | AI-generated phishing to steal API keys |
LLM-Specific API Threats
| Threat | Description | Impact |
|---|---|---|
| Prompt injection | Malicious input manipulates LLM behavior | Data exfiltration, unauthorized actions |
| Tool abuse | LLM agents call APIs with elevated privileges | Privilege escalation |
| Data poisoning | Corrupted training data affects API responses | Misinformation, bad recommendations |
| Model extraction | Systematic querying to replicate the model | IP theft |
| Excessive token consumption | Crafted prompts that maximize token usage | Cost attack |
// ❌ LLM with unrestricted tool access
const response = await llm.chat({
tools: [deleteDatabaseTool, sendEmailTool, readAllUsersTool],
// LLM can be prompt-injected to call any of these
});
// ✅ Sandboxed tool access with human approval
const response = await llm.chat({
tools: [readProductsTool, searchTool], // Read-only tools only
toolCallFilter: (call) => {
// Log all tool calls for audit
auditLog.record(call);
// Block sensitive operations
if (call.name.startsWith('delete') || call.name.startsWith('admin')) {
return { blocked: true, reason: 'Requires human approval' };
}
return { blocked: false };
},
});
Supply Chain API Attacks
Third-party APIs can be compromised:
| Vector | Example | Mitigation |
|---|---|---|
| Compromised SDK | Malicious code in npm package update | Lock dependency versions, audit updates |
| API takeover | Domain expires, attacker registers it | Monitor third-party API health |
| Data exfiltration | Third-party SDK sends data to attacker | Review network calls, CSP headers |
| Dependency confusion | Internal package name claimed on public registry | Use scoped packages, registry config |
Defense Layers
Layer 1: Gateway
// API Gateway — first line of defense
// Rate limiting, authentication, IP filtering, request validation
// Example: Express middleware stack
app.use('/api/*',
cors({ origin: allowedOrigins }), // CORS
helmet(), // Security headers
rateLimit({ max: 100, window: '1m' }), // Rate limiting
validateApiKey, // Authentication
requestSizeLimit('1mb'), // Payload limit
requestLogger, // Audit logging
);
Layer 2: Input Validation
import { z } from 'zod';
// Define strict schemas for every endpoint
const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email().max(255),
password: z.string().min(12).max(128),
role: z.enum(['user', 'editor']), // No 'admin' option
});
app.post('/api/users', async (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.issues,
});
}
// result.data is now typed and validated
});
Layer 3: Authorization
// Policy-based authorization
type Permission = 'read:orders' | 'write:orders' | 'delete:orders' | 'admin:users';
const rolePermissions: Record<string, Permission[]> = {
user: ['read:orders'],
editor: ['read:orders', 'write:orders'],
admin: ['read:orders', 'write:orders', 'delete:orders', 'admin:users'],
};
function requirePermission(permission: Permission) {
return (req, res, next) => {
const userPermissions = rolePermissions[req.user.role] || [];
if (!userPermissions.includes(permission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
app.delete('/api/orders/:id',
requireAuth,
requirePermission('delete:orders'),
verifyOwnership('orders'), // BOLA protection
handleDeleteOrder,
);
Layer 4: Monitoring and Detection
// Detect suspicious patterns
const anomalyDetection = {
// Rapid sequential ID access (BOLA attempt)
detectEnumeration(requests: Request[]): boolean {
const idPattern = requests
.filter(r => r.path.match(/\/api\/\w+\/\d+/))
.map(r => parseInt(r.path.split('/').pop()));
// Sequential IDs: [1, 2, 3, 4, 5] = suspicious
const isSequential = idPattern.every((id, i) =>
i === 0 || id === idPattern[i - 1] + 1
);
return isSequential && idPattern.length > 5;
},
// Unusual volume from single source
detectBruteForce(requests: Request[]): boolean {
const perMinute = requests.filter(r =>
Date.now() - r.timestamp < 60000
).length;
return perMinute > 100;
},
};
Security Tools
| Tool | Type | Best For |
|---|---|---|
| 42Crunch | API security platform | Audit, testing, firewall |
| Salt Security | API protection | Runtime protection, threat detection |
| Traceable AI | API security | API discovery, threat analytics |
| Wallarm | API firewall | WAF + API protection |
| Escape | API security testing | Automated security scanning |
| OWASP ZAP | Security scanner (free) | Penetration testing |
| Burp Suite | Security testing | Manual + automated testing |
API Security Checklist
Before Launch
| Check | Priority |
|---|---|
| Authentication on every endpoint | P0 |
| Authorization (ownership) checks | P0 |
| Input validation (Zod/Joi) on all inputs | P0 |
| Rate limiting (per-user and global) | P0 |
| HTTPS only (HSTS enabled) | P0 |
| No sensitive data in URLs | P0 |
| API keys not in client-side code | P0 |
| Error messages don't leak internals | P1 |
| Request size limits | P1 |
| CORS properly configured | P1 |
| SQL injection prevention (parameterized queries) | P0 |
| XSS prevention (output encoding) | P1 |
| Dependency audit (npm audit) | P1 |
| Secrets in environment variables, not code | P0 |
Ongoing
| Check | Frequency |
|---|---|
| Dependency updates | Weekly |
| Security scanning (automated) | Every deploy |
| Access log review | Daily (automated alerts) |
| API key rotation | Quarterly |
| Penetration testing | Annually |
| Shadow API discovery | Monthly |
| Incident response drill | Annually |
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Auth check on route, not resource | BOLA — users access others' data | Check ownership at data layer |
| Sequential IDs | Easy enumeration | Use UUIDs |
| Verbose error messages | Leak stack traces, database info | Generic errors in production |
| No rate limiting | Brute force, DoS | Rate limit per user and per IP |
| API keys in frontend code | Full API access for anyone | Use backend proxy, scoped tokens |
| Trusting client-side validation only | Bypass by calling API directly | Always validate server-side |
| No audit logging | Can't investigate breaches | Log all mutations with user, timestamp, IP |
Find APIs with the strongest security practices on APIScout — security ratings, compliance certifications, and authentication method comparisons.