Information disclosure is one of the quietest categories of API vulnerabilities, and that’s exactly what makes it dangerous. There’s no dramatic exploit, no crash, no obvious error — just an API that tells you a little more than it should. Over time, testing APIs, I’ve learned that these small leaks add up fast, and they’re often the first domino in a much bigger attack chain. In this article, I’ll walk through every place I look for information disclosure when testing an API.
What Counts as Information Disclosure?
Information disclosure happens whenever an API reveals data that the requesting party shouldn’t have access to. This can range from something as small as a software version number to something as serious as another user’s private data or internal system credentials. I split it into a few broad categories:
- Technical disclosure — server versions, stack traces, internal file paths, framework details
- Business logic disclosure — internal pricing logic, unpublished features, admin-only functionality hints
- User data disclosure — personal information belonging to other users
- Credential and secret disclosure — API keys, tokens, internal service URLs
Step 1: Testing Error Messages
This is the first place I look, because it’s the easiest place for developers to slip up. I intentionally send malformed requests to see what comes back:
- Invalid JSON bodies
- Missing required fields
- Wrong data types (sending a string where an integer is expected)
- SQL-breaking characters like a single quote in a text field
- Extremely long input values
I’m looking for responses that include:
{
"error": "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'app_prod.users_backup' doesn't exist",
"trace": "at UserController.php:142"
}
A response like this tells me the database type, table naming conventions, the framework, and even the file path of the code handling the request. That’s a lot of free reconnaissance handed over by a single bad request.
Step 2: Testing HTTP Response Headers
Headers are an underrated source of information disclosure. I always check for:
Server: Apache/2.4.41 (Ubuntu)
X-Powered-By: PHP/7.2.24
X-AspNet-Version: 4.0.30319
These headers tell me exact software versions, which I can then cross-reference against known CVEs. I also check for internal debugging headers accidentally left enabled in production, like X-Debug-Token (common in Symfony apps) which can sometimes link to an exposed debug panel.
Step 3: Testing for Verbose API Responses (Excessive Data in Legitimate Fields)
Sometimes the disclosure isn’t in an error — it’s baked right into a normal, successful response. I check whether an endpoint returns more fields than the frontend actually displays. For example, a /api/users/123 endpoint might return:
{
"id": 123,
"name": "Jane Doe",
"email": "jane@example.com",
"passwordHash": "$2b$10$...",
"internalNotes": "Flagged for chargeback dispute",
"ssn_last4": "1234"
}
Even if the frontend only displays the name, the raw API response might be leaking password hashes, internal notes, or partial sensitive identifiers to anyone who inspects network traffic. I always check the full raw JSON response, not just what’s rendered on screen.
Step 4: Testing for User Enumeration
I test whether an API’s responses let me determine if a specific username, email, or account ID exists, even without full access to that account. Common places this shows up:
- Login endpoints — “Invalid password” versus “User not found” are two different messages that confirm whether an email is registered.
- Password reset endpoints — “If this email exists, a reset link has been sent” is the correct behavior; “Email not found” is a leak.
- Registration endpoints — “This email is already registered” confirms an account exists.
- Timing differences — even when messages are identical, I test whether response times differ measurably between existing and non-existing accounts, since a database lookup that finds a match may take slightly longer.
Step 5: Testing for Directory and Endpoint Disclosure
I look for ways the API accidentally reveals its own structure:
- API documentation left exposed — Swagger/OpenAPI docs at
/swagger.json,/api-docs,/openapi.yamlsometimes reveal internal-only or deprecated endpoints not meant for public use. - Debug/test endpoints —
/api/test,/api/debug,/api/_internalleft accessible in production. - Backup and config files —
/api/.env,/api/config.json.bak,/.git/configaccidentally deployed alongside the API. - Directory listing — if a storage path is misconfigured, requesting the parent directory instead of a specific file might return a full file listing.
Step 6: Testing for Metadata and Comment Leakage
I check API responses (and any accompanying static files) for:
- HTML/JS comments left in served files referencing internal tools, admin panel URLs, or TODO notes about security issues
- Metadata in uploaded files served back by the API (EXIF data in images, author names in PDF/Office document metadata) that the API might return without stripping
- Source maps (
.js.mapfiles) accidentally deployed to production, which let me reconstruct readable source code from minified JavaScript
Step 7: Testing for Cross-User Data Leakage in List Endpoints
I pay close attention to any endpoint returning a list of items — search results, activity feeds, recommendation engines — because these often leak more than single-object endpoints. I test:
- Does a search endpoint return results belonging to other users when the query is broad enough?
- Does a “related items” or “you might also like” feature expose private items belonging to other accounts?
- Does pagination allow me to iterate through IDs and view data meant to be private, even if I can’t see it through the normal UI flow?
Step 8: Testing Third-Party Integrations for Leakage
APIs often pull in data from third-party services (payment processors, analytics, CRM tools). I test whether:
- Webhook payloads sent to or from third parties include more data than necessary.
- API responses accidentally forward raw third-party API responses, including internal identifiers or fields from the third-party system.
- Client-side integration keys (like analytics or payment SDK keys) are more privileged than they should be, letting me use them to query data outside the intended scope.
Step 9: Testing Cached Responses
I check whether sensitive, user-specific API responses are being cached by CDNs or reverse proxies without proper cache-control headers, which could let one user’s cached response be served to another user requesting the same URL.
Cache-Control: private, no-store
If this header is missing on a personalized endpoint, I test by making requests from two different sessions and seeing if a shared cache layer serves stale, cross-user data.
Tools I Use for Information Disclosure Testing
- Burp Suite — for intercepting and comparing full raw responses against what the UI displays
- ffuf / gobuster — for discovering hidden endpoints, backup files, and directories
- truffleHog / gitleaks — for scanning any exposed repositories or source maps for leaked secrets
- ExifTool — for checking metadata in files served by the API
- Wayback Machine / archive.org — for finding old, possibly still-active endpoints that used to be documented publicly
Common Information Disclosure Issues I Keep Finding
- Verbose error messages leaking stack traces and database structure.
- API responses including internal fields never meant for the client (password hashes, internal flags, admin notes).
- Login/registration flows confirming whether an email or username exists.
- Swagger/OpenAPI documentation exposed publicly, revealing undocumented endpoints.
- Source maps deployed to production, exposing readable source code.
- Missing cache-control headers on personalized API responses.
How I Recommend Fixing Information Disclosure Issues
- Return generic, consistent error messages to clients; log the detailed technical error server-side only.
- Explicitly define response schemas (using serializers/DTOs) instead of returning raw database objects.
- Use consistent, non-revealing messaging for login, registration, and password reset flows regardless of whether the account exists.
- Remove or restrict access to API documentation, debug endpoints, and backup files in production.
- Strip metadata from files before serving them back through the API.
- Set
Cache-Control: private, no-storeon any endpoint returning personalized or sensitive data. - Regularly scan your own deployed assets for accidentally exposed source maps,
.envfiles, and git directories.
Final Thoughts
Information disclosure rarely feels like “the big vulnerability” on its own, but I’ve used disclosed information time and again as the starting point for much more serious attacks — confirming valid usernames before a credential stuffing attempt, or using a leaked stack trace to pinpoint the exact framework version to target. Treat every piece of information an API reveals as a potential building block for a bigger attack, because that’s exactly how it gets used.