API Security Testing: How to Find and Fix API Vulnerabilities
A practical guide to API security testing using OWASP API Top 10 as a framework, with Postman, Burp Suite, and 42Crunch tooling and actionable remediation.
API Security Testing: How to Find and Fix API Vulnerabilities
APIs are the attack surface that matters most in modern applications. According to Gartner, APIs account for the majority of web application attacks, and most organizations have APIs they are not actively monitoring. This guide provides a systematic approach to API security testing based on the OWASP API Security Top 10, covering both manual and automated testing techniques.
The OWASP API Security Top 10
The OWASP API Security Top 10 (2023) identifies the most critical API vulnerability categories. Unlike the web application Top 10, the API list reflects the unique characteristics of API architectures — programmatic access, machine-to-machine communication, and fine-grained data operations.
API1: Broken Object Level Authorization (BOLA / IDOR)
BOLA is the most common and impactful API vulnerability. It occurs when an API endpoint accepts an object identifier (user ID, order ID, document ID) and returns or modifies that object without verifying that the requesting user is authorized to access it.
Testing methodology:
- Capture a legitimate request that includes an object ID in the URL or body:
GET /api/invoices/1042 - Change the ID to another user's object:
GET /api/invoices/1041 - Verify whether the response returns data belonging to a different user
Use Burp Suite's Intruder or Repeater to iterate through sequential IDs. For non-sequential UUIDs, test with IDs from a second test account.
Fix:
// Vulnerable
app.get('/api/invoices/:id', async (req, res) => {
const invoice = await Invoice.findById(req.params.id);
res.json(invoice);
});
// Fixed — filter by both ID and authenticated user
app.get('/api/invoices/:id', authenticate, async (req, res) => {
const invoice = await Invoice.findOne({
_id: req.params.id,
userId: req.user.userId // must belong to the requesting user
});
if (!invoice) return res.status(404).json({ error: 'Not found' });
res.json(invoice);
});
API2: Broken Authentication
Test every authentication endpoint for:
- Credential stuffing resistance: No rate limiting, no account lockout
- JWT issues: Weak secrets,
alg:none, missing expiration (see our JWT attacks guide) - API key exposure: Keys in URLs (appear in server logs), overly broad permissions
- Password reset flaws: Predictable tokens, no expiration, user enumeration via response differences
Testing with Burp Suite:
Use Burp's Intruder with a password wordlist to test for missing rate limiting on /api/auth/login. If 1000 requests complete in 30 seconds without any 429 responses or CAPTCHAs, rate limiting is absent.
API3: Broken Object Property Level Authorization (BOPLA)
Similar to BOLA but at the property level. An endpoint returns more fields than the user should see (over-fetching) or accepts more fields in write operations than it should (mass assignment).
Testing for over-fetching:
Compare the fields returned by the API against the fields displayed in the UI. Fields present in the API response but not shown in the UI are candidates for information disclosure — internal IDs, other users' data, or sensitive internal fields.
Testing for mass assignment:
# The UI only sends: {"name": "Alice"}
# Test adding unauthorized fields:
curl -X PATCH /api/users/123 \
-H "Authorization: Bearer user_token" \
-d '{"name": "Alice", "role": "admin", "email": "admin@example.com"}'
If the server applies the role or email change without rejecting those fields, mass assignment is present.
Fix: Use explicit field allowlists in your update operations:
const UPDATABLE_FIELDS = ['name', 'bio', 'avatarUrl'] as const;
app.patch('/api/users/:id', authenticate, async (req, res) => {
const updates = Object.fromEntries(
UPDATABLE_FIELDS
.filter(field => field in req.body)
.map(field => [field, req.body[field]])
);
await User.findOneAndUpdate({ userId: req.params.id }, updates);
});
API4: Unrestricted Resource Consumption
Test for missing rate limiting and resource limits on expensive operations:
- Pagination endpoints with no maximum page size:
GET /api/messages?limit=999999 - File upload endpoints with no size limits
- Batch operation endpoints:
POST /api/emails/sendwith an array of 10,000 recipients - Search endpoints with unbounded results
Use Burp Suite's Intruder or a simple loop in Postman's collection runner to send 100 rapid requests and observe whether rate limiting engages.
API5: Broken Function Level Authorization
Test whether lower-privilege users can access administrative functions. Common patterns:
- Admin endpoints under predictable paths:
/api/admin/users,/api/v1/admin/,/api/internal/ - The same endpoint behaves differently based on the
roleclaim in the JWT — test by modifying the token - HTTP method switching:
GET /api/users/123is allowed butDELETE /api/users/123should be admin-only
Automated discovery:
Use tools like Kiterunner or FFUF with an API-specific wordlist to discover undocumented endpoints:
kr scan https://api.example.com -w routes-large.kite -A=apiroutes-210228
API6-10: Additional Vectors
- API6 (Unrestricted Access to Sensitive Business Flows): Test automated flows — unlimited discount code application, bulk account creation, unlimited content generation
- API7 (Server-Side Request Forgery): Test any parameter that accepts a URL for SSRF (covered in our SSRF guide)
- API8 (Security Misconfiguration): Verify TLS configuration, CORS headers, verbose error messages that expose stack traces or internal paths
- API9 (Improper Inventory Management): Test whether deprecated API versions (
/api/v1/) have the same security controls as current versions - API10 (Unsafe Consumption of APIs): Review how your API consumes third-party APIs — validate and sanitize all data received from external services
Testing Tools
Postman for API Testing
Postman excels at building authenticated test collections. Use environments to manage tokens and base URLs across dev/staging/production.
Writing authorization tests:
// Postman test script — add to the Tests tab
pm.test("Authenticated endpoint requires auth", function() {
// Re-run the request without auth
const unauthRequest = {
url: pm.request.url.toString(),
method: pm.request.method,
header: pm.request.headers.toObject()
};
delete unauthRequest.header['Authorization'];
pm.sendRequest(unauthRequest, function(err, response) {
pm.expect(response.code).to.be.oneOf([401, 403]);
});
});
pm.test("Response does not contain sensitive fields", function() {
const body = pm.response.json();
pm.expect(body).to.not.have.property('password');
pm.expect(body).to.not.have.property('passwordHash');
pm.expect(body).to.not.have.property('stripeSecretKey');
});
Burp Suite for Intercepted Testing
Burp Suite's Proxy captures all API traffic from your browser or mobile app. Key workflows for API testing:
- Repeater: Replay and modify individual requests. Essential for BOLA testing (change IDs), privilege escalation (modify role claims), and BOPLA testing (add unauthorized fields).
- Intruder: Automated fuzzing. Use for rate limit testing, parameter enumeration, and brute-force testing with the Sniper or Cluster Bomb attack modes.
- Logger: Review all requests/responses to spot information disclosure in error messages.
- Active Scanner: Run the active scan against API endpoints to automatically detect injection vulnerabilities, misconfigurations, and missing security headers.
The Burp extension AuthMatrix automates authorization testing across different user roles and HTTP methods, which is particularly useful for BOLA and broken function-level authorization testing.
42Crunch for OpenAPI/Swagger Analysis
42Crunch performs static analysis of your OpenAPI specification to identify security issues before runtime:
npm install -g @42crunch/api-security-audit
42crunch audit --api-key $CRUNCH_KEY openapi.yaml
It checks for:
- Endpoints without authentication definitions
- Missing input validation (no schema constraints on parameters)
- Permissive CORS configurations in the spec
- Sensitive data in paths and responses
Integrate 42Crunch into CI/CD to fail builds when OpenAPI specifications have critical security scores below your threshold.
Building a Security Test Suite
Automate the most reliable API security tests in your CI pipeline:
// Example Postman/Newman test structure
describe('Authorization Controls', () => {
test('Unauthenticated requests return 401', async () => {
const res = await fetch('/api/profile', { method: 'GET' });
expect(res.status).toBe(401);
});
test('User cannot access other user\'s resources', async () => {
// Login as user A, try to access user B's invoice
const token = await loginAs('userA@test.com', 'password');
const res = await fetch('/api/invoices/userB-invoice-id', {
headers: { Authorization: `Bearer ${token}` }
});
expect(res.status).toBe(404); // or 403 — not 200
});
test('User cannot escalate to admin role', async () => {
const token = await loginAs('user@test.com', 'password');
const res = await fetch('/api/users/me', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ role: 'admin' })
});
const body = await res.json();
expect(body.role).toBe('user'); // role should not have changed
});
});
Running these tests on every pull request catches regressions in authorization logic before they reach production. Combined with regular manual testing and static analysis of your OpenAPI specification, this provides layered API security coverage.