Skip to content

Information disclosure

CWE-200OWASP A01:2021Updated August 31, 20266 min read

Information disclosure is a vulnerability in which an application reveals more than the user is allowed to see: stack traces, debug endpoints, a deployed .git directory or API fields the interface hides. On its own it rarely causes direct damage, but it hands an attacker the knowledge for the next step. The fix is generic error messages plus responses you shape field by field.

Applications routinely say more than their builders intended: a stack trace after an unexpected error, a forgotten .git directory beside the web root, an API returning the whole database record while the screen shows three fields. Individually they look unspectacular, but together they hand an attacker the knowledge for the next move. Here is how information disclosure happens and how to close it.

What is information disclosure?

Information disclosure, also called information leakage, is a vulnerability in which an application reveals technical or personal data to someone who has no right to it. It is not one specific bug but a category: everything a system unintentionally says about itself or its users.

An everyday comparison: a shop with the stockroom door left ajar. Nothing goes missing while nobody walks in, but anyone passing by sees which safe is in there. The break-in comes later.

In practice the leak comes from a handful of familiar places:

  • stack traces and verbose error messages exposing the framework, a version number, an absolute path or a failed query;
  • debug endpoints still reachable in production: a profiler, a status page listing the full configuration, an index of routes;
  • development files shipped with the deployment: a .git directory, an .env file or a backup beside the web root;
  • source maps, which translate the minified frontend back into the original source, comments included;
  • API responses carrying more fields than the interface displays, because the whole model is serialised at once.

In the OWASP Top 10 this falls under A01:2021 Broken Access Control, classified as CWE-200.

How does an information disclosure attack work?

An attacker rarely opens with an exploit but with reconnaissance, and information disclosure is the cheapest source. The most common case is also the least visible: a portal that correctly checks who may request a user record, then returns it in full.

Vulnerable:

// The entire model goes out over the wire
app.get("/api/users/:id", async (req, res) => {
  const user = await db.users.findById(req.params.id);
  res.json(user);
});

// And the error handler ships the stack trace with it
app.use((err, req, res, next) => {
  res.status(500).json({ message: err.message, stack: err.stack });
});

The interface renders only the name and the role, so nothing looks wrong in the browser. The response carries more:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 42,
  "name": "J. Jansen",
  "email": "j.jansen@example.com",
  "passwordHash": "$2b$12$Q7l0mR4...",
  "totpSecret": "JBSWY3DPEHPK3PXP",
  "role": "admin",
  "internalNote": "account referred to debt collection"
}

The attacker forces nothing: they open the developer console and read the response for their own account. The hash field enables offline cracking, the TOTP secret (time-based one-time password, the code from the authenticator app) makes the second factor worthless, and the internal note is personal data.

The error handler does the same with technical information. A request with a parameter of the wrong type returns a stack trace with the absolute path, the ORM in use and its version, sometimes the failed query. That is what turns blind guessing into a targeted attack.

Outside the API the pattern repeats. Deploying by copying a working directory puts the whole version history online. One request reveals it:

curl -s https://app.example.com/.git/HEAD
# ref: refs/heads/main

If that line comes back instead of a 404, the repository can be reconstructed, including old commits holding a key tidied away later. Source maps follow the same logic: a .map file beside your bundle hands back the unprocessed source.

The safe version rests on two principles: every response is shaped field by field (response shaping), and errors go out generically while the detail stays in the log.

Secure:

// Field allowlist: anything not listed here never leaves the server
const publicUser = (user) => ({
  id: user.id,
  name: user.name,
  role: user.role
});

app.get("/api/users/:id", async (req, res) => {
  const user = await db.users.findById(req.params.id);
  if (!user) return res.status(404).json({ error: "Not found" });
  res.json(publicUser(user));
});

app.use((err, req, res, next) => {
  const ref = crypto.randomUUID();
  logger.error({ ref, err });
  res.status(500).json({ error: "Internal server error", ref });
});

The user receives a short message with a reference number, which support looks up in the log to find the full stack trace. The important detail is that the list is positive: a new database column does not silently appear in the API, whereas a list of fields to hide breaks with every schema change.

Hiding fields in the interface is not a control: the response still contains the data. Assume, too, that anything which has ever leaked is public. A key that appeared in a stack trace, a source map or a .git directory must be rotated; deleting the file does not undo a search engine’s cached copy.

What is the impact of information disclosure?

Severity varies widely, which is why this category is so often waved through. An X-Powered-By header or a version number on an error page is low on its own, because no data is lost. It still tells an attacker which exploits are worth trying.

In the middle sit the cases that enable another attack. A stack trace containing a failed SQL query speeds up the exploitation of an injection, and a different message for an unknown user than for a wrong password reveals which email addresses have accounts, feeding phishing and credential stuffing.

At the top end the impact is high. An API that hands personal data to someone not entitled to it is a data breach under the GDPR, with a notification duty to the supervisory authority. A reachable .git directory or an .env file holding database passwords is no longer a leak but a keyring. That range, from low to high, is why every finding has to be judged on its own content.

How do you detect information disclosure?

Detection starts by provoking errors. Send a letter where a number is expected, or omit a required parameter, and look at what comes back. A well behaved application returns a short message, a leaking one a stack trace.

Then work the familiar path list: /.git/HEAD, /.env, /server-status, /actuator, /debug and backups named after the domain. Check the Server and X-Powered-By response headers, and search the frontend bundle for .map references and comments that survived the build.

The most valuable findings, however, do not come out of a scanner. Put an API response next to the screen that consumes it and count the fields: anything the interface does not display is a candidate. Then call the same endpoint as an administrator and as an ordinary user; identical responses mean the API leaks admin-only fields. That requires understanding of the data model, which is where scanners stall. AssistSec covers this ground during a penetration test and records which field or file was exposed.

How do you prevent information disclosure?

  • Turn debug mode off in production and return generic errors. A short message with a reference number for the user, the detail in the log only.
  • Shape every response explicitly. Build the output from a fixed list of fields, through a DTO (data transfer object) or a serialiser, not by serialising the database model.
  • Keep development files out of the web root. Deploy a build artefact rather than a working directory, and block .git and .env in the web server or CDN.
  • Do not publish source maps. Upload them to your error monitoring instead of leaving them beside the bundle.
  • Lock down debug and management interfaces. Profilers, metrics and health endpoints and admin routes belong behind authentication or segmentation.
  • Limit what your infrastructure and your messages announce. Disable server banners and directory listing, and use one login message for existing and non-existing accounts.
  • Treat leaked data as public. Rotate exposed keys and confirm the correction with a retest.

Sources

Frequently asked questions

Is information disclosure a real vulnerability?

Yes, although the severity varies enormously. A version number in a response header is harmless on its own, while an API that ships password hashes or personal data is a breach with a notification duty attached. The rule of thumb is simple: data the user does not need and is not allowed to see should never leave the server.

Why is an exposed .git directory dangerous?

Because the directory holds the full version history, not just the code you are running today. An attacker can reconstruct the source and read old commits that still contain keys, passwords or internal endpoints someone later removed. Request /.git/HEAD to check: if a branch reference comes back instead of a 404, the directory is exposed.

Should I remove source maps from production?

At the very least, do not publish them next to your bundle. A source map translates minified code back into the original files, comments and sometimes configuration included. If you want readable stack traces in your error monitoring, upload the source maps straight to that service rather than deploying them with the application.

What is the difference between information disclosure and IDOR?

With IDOR (insecure direct object reference) an attacker changes a reference, such as a record id in the URL, and receives another user's data. With information disclosure they change nothing: the application volunteers too much, for example extra fields in a response they were entitled to request anyway. Both sit under broken access control and often surface together during a test.

Related articles

Press / to search · Esc