Postman API Security Testing: Finding Vulnerabilities in Your API Collections
Most API vulnerabilities are discovered the same way: someone sends a request they weren't supposed to send and the server does something it wasn't supposed to do. Postman is sitting right there in every developer's workflow, ready to send exactly those requests. The question is whether your team is using it to find those issues before attackers do.
Postman has over 30 million users. Almost every development team that builds APIs has it installed. API collections are painstakingly maintained, documenting every endpoint, parameter, and expected response. That collection is also a complete map of your attack surface — and most teams aren't using it that way.
This guide covers how to turn your existing Postman collections into a structured security testing workflow: setting up environments for security testing, writing test scripts that catch authorization flaws, testing the OWASP API Security Top 10, and running injection tests. We'll also be direct about where Postman ends and you need a proper DAST scanner.
Why Postman Is a Security Testing Tool, Not Just a Dev Tool
The standard framing is that Postman is for API development — building, documenting, and sharing APIs. Security testing is something you do separately, with different tools, at a different stage of the cycle. That framing is wrong, and it costs teams time and money.
The functional test you wrote to verify that GET /users/123 returns the correct user is one request modification away from a security test: change 123 to 124 and check whether the server returns a different user's data. If it does, you've just found a BOLA (Broken Object Level Authorization) vulnerability — the single most common API security issue in the OWASP API Security Top 10.
The difference between functional testing and security testing in Postman is intent and methodology, not tooling. Functional tests verify that valid inputs produce expected outputs. Security tests verify that invalid, unexpected, or unauthorized inputs are correctly rejected. The mechanism — constructing HTTP requests and inspecting responses — is identical.
What Postman gives you that other security tools don't
When you're security testing with Burp Suite or a DAST scanner against an API you're not familiar with, you spend significant time reverse-engineering the API structure: what endpoints exist, what parameters they accept, what authentication scheme they use. Your Postman collection already has all of that. You have authenticated request examples, documented request bodies, and known-good baseline responses to diff against.
That context is extremely valuable. A DAST scanner fuzzing parameters blindly will find some things, but it won't know that userId in the request body should match the authenticated user's ID, or that the role field in a PUT request body shouldn't be writable by regular users. You know that because you built the API. Security testing with Postman is about applying that insider knowledge systematically.
When Postman is sufficient vs when you need more
Postman is the right tool for:
- Authorization boundary testing (who can access what)
- Authentication mechanism testing (what happens with bad/missing/expired tokens)
- Input validation testing against known parameters
- Business logic vulnerability testing
- Mass assignment and object property exposure testing
Postman is not the right tool for:
- Automated crawling of undocumented endpoints
- Fuzzing — testing thousands of payload variations systematically
- Passive scanning of traffic for security issues
- Continuous, scheduled testing in CI/CD without significant setup
- Attack chaining across multiple vulnerabilities
The practical answer for most teams is: use Postman for developer-driven security testing during development, and use a DAST scanner for continuous coverage. They're complementary, not alternatives.
Setting Up Postman for Security Testing
Before you start sending malicious payloads, you need to organize your environment correctly. Security testing against the wrong environment or with production credentials is a bad day waiting to happen.
Environments: separate dev, staging, and security testing
Create a dedicated security testing environment in Postman. This environment should point at a staging or test instance of your API — never production. Populate it with:
baseUrl— the API base URL for your test environmentuserToken— a valid token for a regular user accountadminToken— a valid token for an admin accountotherUserToken— a valid token for a second regular user account (critical for IDOR testing)expiredToken— a token that has passed its expiry timeuserId,otherUserId— IDs corresponding to each user account
Having two distinct user accounts is non-negotiable for authorization testing. Without a second account, you can't test whether account A can access account B's resources.
Pre-request scripts for automated authentication
Many APIs use short-lived access tokens that expire during a test session. Writing a pre-request script that fetches a fresh token before each request keeps your tests from failing due to auth issues rather than actual security findings.
// Pre-request script: auto-refresh JWT token if expired
const tokenExpiry = pm.environment.get('tokenExpiry');
const now = Date.now();
// Refresh if token expires in less than 60 seconds
if (!tokenExpiry || now > (parseInt(tokenExpiry) - 60000)) {
const loginRequest = {
url: pm.environment.get('baseUrl') + '/auth/login',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({
email: pm.environment.get('testUserEmail'),
password: pm.environment.get('testUserPassword')
})
}
};
pm.sendRequest(loginRequest, function(err, response) {
if (err) {
console.error('Auth refresh failed:', err);
return;
}
const body = response.json();
pm.environment.set('userToken', body.access_token);
// Store expiry timestamp (assuming expires_in is in seconds)
if (body.expires_in) {
pm.environment.set('tokenExpiry', Date.now() + (body.expires_in * 1000));
}
});
}
Add this at the collection level so it runs before every request in the collection. Use {{userToken}} in your Authorization header.
Organizing collections by attack surface
Rather than organizing your security testing collection by endpoint (the way dev collections are typically organized), organize it by attack type. A structure that works well:
- Authentication Tests — missing auth, expired tokens, invalid tokens, token manipulation
- Authorization Tests — IDOR, privilege escalation, cross-account access
- Input Validation — injection payloads for each input type
- Rate Limiting — requests to endpoints that should throttle
- Business Logic — endpoint-specific logic flaws
This makes it much easier to run a targeted sweep when you're reviewing a specific concern, and to hand off the collection to another engineer who can run the tests without understanding every endpoint.
Authentication and Authorization Testing
Authentication and authorization flaws are the most impactful API vulnerabilities, and they're among the easiest to test manually. They don't require fuzzing or payload lists — just disciplined request modification.
Testing for missing authentication
For every endpoint in your API, test what happens when you remove the Authorization header entirely. The expected behavior is a 401 Unauthorized response. What you'll sometimes find instead:
- 200 OK with data — authentication not enforced
- 403 Forbidden — authentication enforced but error code is wrong (minor, not critical)
- 500 Internal Server Error — the server tried to look up a null user and crashed
Write a Postman test script that asserts the correct rejection:
// Test: unauthenticated request must be rejected
pm.test("Unauthenticated request returns 401", function() {
pm.expect(pm.response.code).to.equal(401);
});
pm.test("Response does not contain user data", function() {
const body = pm.response.json();
// Adjust these field names to match your data model
pm.expect(body).to.not.have.property('email');
pm.expect(body).to.not.have.property('userId');
pm.expect(body).to.not.have.property('data');
});
To remove authentication for a single request while keeping it on others in the collection, set the request's Authorization to "No Auth" — this overrides the collection-level setting for that request only.
Horizontal privilege escalation: IDOR testing
Horizontal privilege escalation means accessing another user's resources at the same privilege level. The test is straightforward: authenticate as User A, then request a resource belonging to User B.
In Postman, you'll have {{userId}} (User A's ID) and {{otherUserId}} (User B's ID) in your environment. Create a duplicate of any endpoint that returns user-specific data and swap the ID:
// Request: GET {{baseUrl}}/users/{{otherUserId}}/profile
// Authorization: Bearer {{userToken}} (User A's token, requesting User B's profile)
pm.test("Cannot access another user's profile", function() {
pm.expect(pm.response.code).to.be.oneOf([403, 404]);
});
pm.test("Response does not contain target user's data", function() {
if (pm.response.code === 200) {
// If the server returned 200, check the body doesn't contain the other user's data
const body = pm.response.json();
const otherUserId = pm.environment.get('otherUserId');
pm.expect(body.userId || body.id).to.not.equal(otherUserId);
}
});
Vertical privilege escalation: accessing admin endpoints
Vertical privilege escalation means a lower-privileged user accessing functionality intended for higher-privileged users. If your API has admin endpoints (e.g., /admin/users, /internal/reports, /api/v1/management/settings), test them with a regular user token.
Admin endpoints are often undocumented, which is why developers assume they're protected by obscurity. They're not. Test every endpoint in your admin or management namespace with a regular user token and verify you get a 403.
Token expiry testing
Store an expired token in your environment variable expiredToken. Run your standard authenticated requests using this token. The server should reject all requests with a 401. If it accepts them, your token validation is broken — either the expiry check isn't implemented or isn't being enforced at the middleware layer.
JWT manipulation testing
If your API uses JWTs, the claims inside the token can be tampered with if signature validation is weak. Common JWT attacks:
- Algorithm confusion (none attack): Change the
algheader tononeand strip the signature - Algorithm confusion (RS256 to HS256): If the server accepts HS256 and you have the public key, sign with it as an HMAC secret
- Claim tampering: Decode the payload, change
userIdorrole, re-encode (without changing signature) and send
Decode the JWT payload in a Postman pre-request script to inspect and modify claims:
// Pre-request script: decode JWT and log claims (for inspection)
const token = pm.environment.get('userToken');
if (token) {
const parts = token.split('.');
if (parts.length === 3) {
// Base64url decode the payload
const payload = parts[1];
const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
console.log('JWT claims:', JSON.stringify(decoded, null, 2));
console.log('Token expires:', new Date(decoded.exp * 1000).toISOString());
// Store claims as environment variables for use in tests
pm.environment.set('jwtUserId', decoded.sub || decoded.userId);
pm.environment.set('jwtRole', decoded.role || decoded.roles);
}
}
For actual manipulation (changing claims and re-signing), you'll want a dedicated JWT tool like jwt.io or a Burp Suite extension. Postman can send the manipulated token — it just can't re-sign it.
Testing for OWASP API Security Top 10 with Postman
The OWASP API Security Top 10 covers the most common and impactful API vulnerabilities. Postman can test several of them directly.
API1:2023 — Broken Object Level Authorization (BOLA)
BOLA is IDOR at the API level. The test pattern: access a resource with a valid token but using another user's resource ID.
// Test collection for BOLA
// Request 1: Get your own order
GET {{baseUrl}}/orders/{{myOrderId}}
Authorization: Bearer {{userToken}}
// Request 2: Get someone else's order (same token, different ID)
GET {{baseUrl}}/orders/{{otherUserOrderId}}
Authorization: Bearer {{userToken}}
// Test script for Request 2:
pm.test("BOLA: Cannot access another user's order", function() {
pm.expect(pm.response.code).to.be.oneOf([403, 404]);
});
pm.test("BOLA: Response does not leak order data", function() {
if (pm.response.code === 200) {
const body = pm.response.json();
// Flag as finding if server returned the other user's order
pm.expect(body.userId || body.customerId).to.not.equal(
pm.environment.get('otherUserId')
);
}
});
Run this pattern for every resource type in your API: users, orders, documents, files, reports, invoices. BOLA is typically not a systemic fix — each resource type may have its own authorization check that needs testing independently.
API2:2023 — Broken Authentication
Beyond the basic tests above, check for:
- Weak password policy: submit registration requests with passwords like
a,123456,password - No account lockout: send 20+ failed login attempts and verify the account gets locked
- Credential stuffing surface: check whether the login error response distinguishes between "wrong email" and "wrong password" (it shouldn't)
// Test: check for account enumeration via different error messages
pm.test("Login error should not reveal whether email exists", function() {
const body = pm.response.json();
// Both 'email not found' and 'wrong password' should return same generic message
const message = (body.message || body.error || '').toLowerCase();
pm.expect(message).to.not.include('email not found');
pm.expect(message).to.not.include('user not found');
pm.expect(message).to.not.include('no account');
});
API3:2023 — Broken Object Property Level Authorization (BOPLA)
BOPLA covers two related issues: mass assignment (writing properties you shouldn't be able to write) and excessive data exposure (reading properties you shouldn't be able to read).
For mass assignment, test by adding privileged properties to request bodies:
// Test: mass assignment on user update endpoint
// Normal request: PATCH {{baseUrl}}/users/{{userId}}
// Body includes only what the API docs say is writable:
{
"displayName": "Test User",
"role": "admin", // Should not be writable by regular users
"isVerified": true, // Should not be writable by regular users
"credits": 99999, // Should not be writable directly
"subscription": "enterprise" // Should require separate billing flow
}
// Test script:
pm.test("Mass assignment: privileged fields rejected or ignored", function() {
// Server should either reject (400/403) or silently ignore the extra fields
if (pm.response.code === 200) {
const body = pm.response.json();
pm.expect(body.role).to.not.equal('admin');
pm.expect(body.isVerified).to.not.equal(true);
pm.expect(body.credits).to.not.equal(99999);
}
});
For excessive data exposure, inspect successful responses and check whether they contain fields that shouldn't be visible to the requesting user: internal flags, other users' data, hashed passwords, API keys, internal metadata.
API4:2023 — Unrestricted Resource Consumption (Rate Limiting)
Postman's Collection Runner is the right tool here. Set up a collection with a single request to a rate-limited endpoint (login, password reset, OTP verification), then run it with Collection Runner set to 50-100 iterations with no delay.
// Test script: check for rate limiting headers and response codes
pm.test("Rate limiting is enforced after repeated requests", function() {
// By iteration ~20-50, server should start throttling
const iteration = pm.info.iteration;
if (iteration > 20) {
// Either 429 Too Many Requests or throttled response time
const isThrottled = pm.response.code === 429;
const hasRetryAfter = pm.response.headers.has('Retry-After');
const hasRateLimitHeaders = pm.response.headers.has('X-RateLimit-Remaining');
pm.expect(isThrottled || hasRetryAfter || hasRateLimitHeaders).to.be.true;
}
});
// Log response time to spot degradation patterns
console.log(`Iteration ${pm.info.iteration}: ${pm.response.responseTime}ms, status: ${pm.response.code}`);
API5:2023 — Broken Function Level Authorization (BFLA)
BFLA is about accessing privileged functions, not just resources. Admin functionality exposed via the same API surface is a common finding. Test by calling administrative, management, or internal endpoints with a regular user token.
Common patterns to check:
DELETE /users/:id— can a regular user delete other users?POST /users/:id/ban— can a regular user ban accounts?GET /admin/dashboard— is admin UI backed by its own API endpoint?POST /subscriptions/grant— can a user grant themselves a subscription?GET /internal/metrics— are internal telemetry endpoints accessible?
// Test script: verify privileged function requires admin role
pm.test("BFLA: Administrative function requires admin role", function() {
// Regular user token should not be able to access admin functions
pm.expect(pm.response.code).to.be.oneOf([403, 404]);
});
pm.test("BFLA: Response does not reveal admin data", function() {
if (pm.response.code === 200) {
const body = pm.response.json();
// Log as finding — a 200 here is a vulnerability
console.warn("POTENTIAL BFLA FINDING: Admin endpoint returned 200 for regular user");
pm.expect.fail("Admin endpoint accessible to regular user");
}
});
Injection Testing via Postman
Injection testing in Postman is a matter of building parameterized request variants with known payloads. You won't achieve the coverage of a fuzzer running thousands of mutations, but you can hit the common patterns.
SQL injection in API parameters
Test query parameters and request body fields with SQL injection payloads. Start with the basics that break poorly-parameterized queries:
// Create a data file for Collection Runner with injection payloads
// payloads.json:
[
{"payload": "'"},
{"payload": "''"},
{"payload": "' OR '1'='1"},
{"payload": "' OR '1'='1' --"},
{"payload": "' OR 1=1 --"},
{"payload": "1; DROP TABLE users--"},
{"payload": "' UNION SELECT null,null,null--"},
{"payload": "1' AND SLEEP(5)--"},
{"payload": "1; WAITFOR DELAY '0:0:5'--"}
]
// Request using the payload variable:
GET {{baseUrl}}/users/search?q={{payload}}
Authorization: Bearer {{userToken}}
// Test script:
pm.test("SQL injection: no database error exposed", function() {
const body = pm.response.text();
const errorPatterns = [
/sql syntax/i,
/mysql_fetch/i,
/ORA-\d{5}/i,
/SQLite3::/i,
/PostgreSQL.*ERROR/i,
/Unclosed quotation mark/i,
/syntax error/i
];
errorPatterns.forEach(pattern => {
pm.expect(body).to.not.match(pattern);
});
});
pm.test("SQL injection: response time not anomalous (time-based detection)", function() {
// Flag if response takes >4 seconds (suggests successful time-based injection)
pm.expect(pm.response.responseTime).to.be.below(4000);
});
NoSQL injection in JSON bodies
MongoDB and other NoSQL databases are vulnerable to operator injection. If your API accepts JSON bodies and queries a NoSQL database, test with MongoDB query operators:
// NoSQL injection payloads in JSON body
// Normal login request:
{
"email": "user@example.com",
"password": "correcthorsebattery"
}
// NoSQL injection attempt 1: bypass with $ne operator
{
"email": "admin@example.com",
"password": {"$ne": "anything"}
}
// NoSQL injection attempt 2: $gt operator
{
"email": "admin@example.com",
"password": {"$gt": ""}
}
// NoSQL injection attempt 3: $regex to enumerate users
{
"email": {"$regex": ".*@yourdomain.com"},
"password": {"$ne": "x"}
}
// Test script:
pm.test("NoSQL injection: operator injection rejected", function() {
// If these requests return 200 with a valid session, that's a critical finding
pm.expect(pm.response.code).to.not.equal(200);
});
pm.test("NoSQL injection: body properly validated", function() {
if (pm.response.code === 200) {
const body = pm.response.json();
pm.expect(body).to.not.have.property('token');
pm.expect(body).to.not.have.property('accessToken');
console.error("CRITICAL: NoSQL injection may have succeeded");
}
});
Command injection in file-handling endpoints
Endpoints that process filenames, file paths, or shell-adjacent operations are worth testing for command injection. Common injection points: file upload filename parameters, archive extraction paths, image processing parameters, report generation with user-controlled filenames.
// Command injection payloads in filename field
// Test 1: Basic command separator
POST {{baseUrl}}/files/upload
Content-Type: multipart/form-data
filename: "test; id"
filename: "test | id"
filename: "test && id"
filename: "test`id`"
filename: "$(id)"
filename: "../../../etc/passwd"
filename: "..%2F..%2F..%2Fetc%2Fpasswd"
// Test script: check response for command output or path traversal success
pm.test("Command injection: no shell output in response", function() {
const body = pm.response.text();
pm.expect(body).to.not.match(/uid=\d+/); // id command output
pm.expect(body).to.not.match(/root:x:0:0/); // /etc/passwd content
pm.expect(body).to.not.match(/\[sudo\]/i);
});
pm.test("Path traversal: cannot read system files", function() {
const body = pm.response.text();
pm.expect(body).to.not.match(/root:.*:0:0:/);
pm.expect(body).to.not.match(/\[extensions\]/); // Windows ini files
});
Automated Security Tests with Postman Scripts
Individual security tests are useful. A comprehensive test script that runs against every response is more useful. The goal is to add a standard security assertion layer to your existing collection that catches common issues passively — without you needing to think about security for each endpoint.
A comprehensive security test script for collection-level use
Add this to your collection's Tests tab (not individual requests) to run on every response:
// Collection-level security test script
// Runs on every response in the collection
// ---- Sensitive data exposure ----
const responseBody = pm.response.text();
pm.test("No private keys or secrets in response", function() {
pm.expect(responseBody).to.not.match(/-----BEGIN (RSA |EC )?PRIVATE KEY-----/);
pm.expect(responseBody).to.not.match(/-----BEGIN OPENSSH PRIVATE KEY-----/);
pm.expect(responseBody).to.not.match(/sk_live_[a-zA-Z0-9]{24,}/); // Stripe live key
pm.expect(responseBody).to.not.match(/AKIA[0-9A-Z]{16}/); // AWS access key
pm.expect(responseBody).to.not.match(/ghp_[a-zA-Z0-9]{36}/); // GitHub PAT
});
pm.test("No internal stack traces exposed", function() {
pm.expect(responseBody).to.not.match(/at Object\./);
pm.expect(responseBody).to.not.match(/at Module\._compile/);
pm.expect(responseBody).to.not.match(/Traceback \(most recent call last\)/);
pm.expect(responseBody).to.not.match(/java\.lang\.\w+Exception/);
});
pm.test("No internal paths exposed", function() {
pm.expect(responseBody).to.not.match(/\/home\/\w+\//);
pm.expect(responseBody).to.not.match(/C:\\Users\\\w+\\/);
pm.expect(responseBody).to.not.match(/\/var\/www\//);
pm.expect(responseBody).to.not.match(/\/srv\/app\//);
});
// ---- Security headers (for APIs that return HTML or are web-facing) ----
pm.test("Security headers present", function() {
const contentType = pm.response.headers.get('Content-Type') || '';
if (contentType.includes('text/html')) {
pm.expect(pm.response.headers.has('X-Frame-Options') ||
pm.response.headers.has('Content-Security-Policy')).to.be.true;
}
});
pm.test("No verbose server header", function() {
const server = pm.response.headers.get('Server') || '';
// Should not reveal version numbers
pm.expect(server).to.not.match(/Apache\/\d/);
pm.expect(server).to.not.match(/nginx\/\d/);
pm.expect(server).to.not.match(/Microsoft-IIS\/\d/);
});
// ---- Response time side-channel ----
const responseTime = pm.response.responseTime;
if (responseTime > 5000) {
console.warn(`Slow response: ${responseTime}ms on ${pm.request.url} — possible time-based injection or DoS surface`);
}
// ---- PII exposure check ----
pm.test("No obvious PII in error responses", function() {
if (pm.response.code >= 400) {
// Error responses shouldn't contain other users' PII
pm.expect(responseBody).to.not.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i);
pm.expect(responseBody).to.not.match(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/); // Phone numbers
}
});
Using Newman for CI/CD integration
Newman is Postman's CLI runner. It lets you run your security test collection as part of a CI/CD pipeline — no GUI required.
# Install Newman
npm install -g newman
# Export your collection and environment from Postman
# Then run:
newman run security-tests-collection.json \
--environment security-testing-env.json \
--reporters cli,json \
--reporter-json-export ./security-test-results.json \
--bail # Stop on first failure (or remove for full run)
# In GitHub Actions:
# - name: Run API Security Tests
# run: |
# newman run ./tests/security-collection.json \
# --environment ./tests/staging-env.json \
# --reporters cli,junit \
# --reporter-junit-export ./test-results/security.xml
# The JUnit output integrates with GitHub Actions test reporting
One important caveat: Newman running security tests in CI creates a challenge — you need valid test tokens in your environment file, which means secrets in your repo or CI environment variables. Handle these the same way you handle other CI secrets: use environment variable injection, not hardcoded values in exported environment files.
IDOR and Mass Assignment Testing
IDOR and mass assignment deserve their own focused section because they're so common and so underestimated.
Building a systematic IDOR test collection
For every endpoint that takes a resource ID — in the path, as a query parameter, or in the request body — you need an IDOR test variant. The test is always the same pattern: use a valid token for User A, request or modify a resource belonging to User B.
Build this as a separate folder in your security collection. Duplicate the relevant requests from your functional collection, rename them with "IDOR:" prefix, and swap the IDs. Your test script should assert a 403 or 404 response.
Don't forget non-obvious ID fields:
- Nested resource IDs:
/users/{{userId}}/documents/{{otherUserDocId}} - IDs in request bodies:
{ "shareWithUserId": "{{otherUserId}}" } - IDs in filter parameters:
GET /reports?userId={{otherUserId}} - IDs in file download paths:
GET /exports/{{otherUserExportId}}
Testing object property exposure (BOPLA)
There's frequently a gap between what your API documentation says a response contains and what it actually returns. The documentation might show five fields; the actual response includes twelve. Those extra fields are often internal flags, admin metadata, or data from joined database queries that the developer forgot to filter.
Test this by inspecting the full response body for every endpoint — not just the fields your client uses. Write a Postman test that logs all top-level response keys and flags any you don't expect:
// Test: flag unexpected response fields
pm.test("Response only contains documented fields", function() {
const body = pm.response.json();
const documentedFields = ['id', 'email', 'displayName', 'createdAt', 'updatedAt'];
if (body && typeof body === 'object' && !Array.isArray(body)) {
const responseFields = Object.keys(body);
const unexpectedFields = responseFields.filter(f => !documentedFields.includes(f));
if (unexpectedFields.length > 0) {
console.warn('Unexpected fields in response:', unexpectedFields.join(', '));
// Decide whether to fail or warn based on sensitivity
// pm.expect.fail(`Undocumented fields: ${unexpectedFields.join(', ')}`);
}
}
});
This test is most useful when you run it and then review the console output. Fields that consistently appear but aren't documented warrant investigation — they may be harmless internal flags or they may be data that should never leave the server.
The documentation-reality gap
APIs are tested against their documentation. But the implementation is what runs in production. Common gaps:
- An endpoint accepts a
rolefield that the documentation doesn't mention (mass assignment vector) - A response includes a
passwordHashorinternalScorefield not shown in docs - An endpoint listed as requiring admin auth doesn't enforce it consistently
- A deprecated v1 endpoint still works and has weaker authorization than the v2 equivalent
Postman can test all of these — but only if you test what the API actually does, not just what it's supposed to do. Send requests the documentation says shouldn't work. Check responses for fields the documentation doesn't mention.
Limitations: Where Postman Ends and DAST Begins
Postman is a capable security testing tool, but it has genuine limitations. Understanding them helps you allocate effort correctly.
What Postman cannot do
Crawling and discovery. Postman only knows about the endpoints you've added to your collection. If your API has undocumented endpoints — legacy endpoints, debug endpoints, misconfigured routes — Postman won't find them. A DAST scanner combined with traffic analysis or OpenAPI spec crawling can discover endpoint surface that your collection doesn't cover.
Systematic fuzzing. You can run injection tests against documented parameters, but a fuzzer will test every parameter with hundreds of payload variants automatically, including parameters you didn't think to test. The difference in coverage is significant. Postman with a CSV payload file gets you maybe 10-20 payloads per parameter; a fuzzer gets you thousands.
Passive scanning. Tools like Burp Suite sit in the request path and analyze every request and response for security issues as you use the application. Postman doesn't do this — you have to actively construct each security test. This means your coverage is limited to what you thought to test.
Rendered page behavior. If your API is consumed by a web frontend and the security issue involves how the browser renders the response (XSS, clickjacking, CSP violations), Postman won't see it. It's testing the raw HTTP layer.
The manual overhead problem
Every Postman security test requires someone to build it, maintain it as the API evolves, and run it. When you add a new endpoint to your API, someone has to remember to also add the corresponding security tests. That's a process problem — it depends on discipline and doesn't scale well.
A DAST scanner that operates on your OpenAPI specification or proxied traffic automatically covers new endpoints as they're added. There's no "remember to add the security test" step because discovery is automated.
When to hand off to a proper DAST scanner
The practical division of labor:
| Testing Scenario | Postman | DAST Scanner |
|---|---|---|
| Verifying a specific auth fix | Right tool — targeted, fast | Overkill |
| IDOR testing known endpoints | Right tool with good collections | Complementary |
| Regression testing after deployment | Works, but requires maintenance | Better — scheduled, automated |
| Full API surface coverage | Limited to documented endpoints | Right tool — crawls and discovers |
| Continuous security monitoring | Requires CI/CD integration overhead | Right tool — runs on schedule |
| Fuzzing for unknown injection points | Very limited | Right tool — automated payload testing |
| Pre-release security check | Good for known-surface validation | Better for full coverage |
The teams that find the most vulnerabilities before production use both. Postman catches the authorization and logic flaws that require inside knowledge of the application — the kind of knowledge developers have. DAST catches the injection classes, the undiscovered endpoints, and the coverage gaps that come from testing at scale.
The DAST advantage: scheduled, continuous coverage
The most important thing a DAST scanner does that Postman can't is run continuously without human involvement. Security posture degrades over time — new code ships, dependencies change, configurations drift. A scanner that runs every week against your staging environment will catch regressions that a point-in-time Postman test won't see.
Think of Postman security testing as what you do during development, and DAST as what you do continuously after deployment. The development-time tests validate the security properties you designed in. The continuous DAST validates that those properties didn't erode in the last sprint.
The most common API security finding in professional penetration tests is an authorization check that works correctly on the main path but is missing on a secondary endpoint — a bulk operation, an export function, a legacy v1 route — that developers forgot to harden when they wrote the authorization logic on v2. Postman finds this if you thought to test that specific endpoint. DAST finds it because it tests everything.
Start with Postman. Build a security testing habit during development. Use the patterns in this guide to cover authentication, authorization, and injection systematically. When you're ready to add continuous coverage that doesn't depend on developer discipline, that's when you add a DAST scanner to the stack.
Automate what Postman tests manually
Ironimo brings Kali Linux-powered DAST to your CI/CD pipeline — the same API security checks you run manually in Postman, automated and scheduled across every endpoint.
Start free scan