400 Bad Request that earned $$$ - Document-name disclosure via IDOR

400 Bad Request that earned $$$ - Document-name disclosure via IDOR

December 1, 2025 8 min read

How a 400 error exposed workspace and document names due to an IDOR in a document-viewer API - step-by-step reproduction, impact, and defenses.




Disclaimer

This article is written for defensive and educational purposes. All testing described was performed only with authorization or in a controlled context. Never attempt unauthorized access to systems you do not own or have explicit permission to test. The examples below are redacted and safe - they show request/response structure to explain root cause and remediation.


Introduction

APIs often reveal more than developers expect - not because of a classic SQL injection or broken authentication, but because of seemingly innocuous error messages. During a recent assessment Researcher came across a document-viewer API that responded to malformed annotation saves with a 400 Bad Request carrying a human-readable error that included the document name. Chaining this with a simple ID brute-force technique allowed discovery of workspace names and document names that should have remained private.

This post walks through how Researcher discovered it, how to reproduce the behavior safely, why it matters, and - most importantly - how teams should fix this class of leakage so it never happens again.


Understanding the target and the bug at a glance

The application exposes a document viewer API for collaborating on documents inside named workspaces. A POST endpoint handles saving annotations and returns structured JSON on success and failure. Crucially, when supplied with a WorkspaceID and DocumentArtifactID that the caller does not have access to, the API returns an error JSON whose CustomErrorMessage contains the document name. When that happens, an authenticated attacker can enumerate WorkspaceID values and then probe DocumentArtifactID values to recover internal names.

Why this is problematic:

  • Document names are often sensitive (client names, PII hints).
  • Workspace names may reveal business relationships or internal project names.
  • Error-based enumeration is trivial to automate (ffuf/ffuf-style fuzzing).
  • There were no effective rate limits on the discovery endpoint in this case.

Step 1 - Initial reconnaissance (authenticated)

This issue requires an authenticated session. During triage Researcher logged in as a normal user (non-admin) to mimic a real-world attacker who already has valid credentials but should not see other organizations’ artifacts.

My reconnaissance checklist:

  • Map API endpoints around the Document Viewer.
  • Identify annotation/save endpoints that accept JSON payloads.
  • Check whether error messages are redacted or contain friendly strings.
  • Test error responses for leaked metadata.

Researcher found the following relevant endpoints during mapping and proxy inspection:

  • POST /Private.Rest/API/private-processing/v1/workspaces/FUZZ/data-sources/0/field-mappings/private - helpful to brute-force the organization/workspace existence.
  • POST /Private.REST/api/Private-DocumentViewer/v2/workspaces/{WorkspaceID}/annotations/save - the annotation save endpoint that returned CustomErrorMessage values containing document names.

Step 2 - Safe, ethical fuzzing to find a valid WorkspaceID

To avoid noisy, unauthorised tests, do this only inside scope and with credentials you are permitted to use. The pattern is:

  1. Attempt to trigger an identifiable error message when the WorkspaceID does not belong to the logged-in user.
  2. Identify phrases in the returned JSON that include workspace identifiers.

Example request used for discovery (redacted for safety):

POST /Private.Rest/API/private-processing/v1/workspaces/FUZZ/data-sources/0/field-mappings/private HTTP/2
Host: private.program
Cookie: <your user cookie>
Content-Type: application/json

{"fieldsToAppend":[],"dataSourceId":0}
HTTP

If the fuzzed FUZZ value corresponds to a workspace that exists but you cannot access, the server returned a 500 Internal Server Error with a message similar to:

{
  "ErrorType": "Private.Services.Exceptions.ServiceException",
  "Identifier": "",
  "Message": "Logged in user ID 12345 does not have access to workspace ID 1028172. If the logged in user is a member of the system administrators group, please ensure that the logged in user is added to any other group with access to this workspace."
}
HTTP

That response is useful because it confirms the workspace exists (and returns an ID in the message). From there you can target the DocumentViewer endpoint.

Important: This kind of probing is sensitive. Only run it under authorization and throttle your requests.


Step 3 - Probing the annotation save endpoint to reveal workspace names

With a valid WorkspaceID identified (in the above example: 1028172), the annotation save endpoint accepted an annotations JSON array. Submitting an annotation for a document you don’t have access to should fail - but the server’s failure message surprisingly included the document’s human-friendly name.

Example payload:

POST /Private.REST/api/Private-DocumentViewer/v2/workspaces/1028172/annotations/save HTTP/2
Host: private.program
Cookie: <your user cookie>
Content-Type: application/json

{"annotations":[{"ID":43,"X":72.5833,"Y":121,"WorkspaceID":1028172,"DocumentArtifactID":1003663,"MarkupSetArtifactID":1134197}]}
HTTP

For certain DocumentArtifactID values the server responded with a JSON error containing the document name. Example response pattern:

HTTP/2 400 Bad Request
Content-Type: application/json

{
  "Success": false,
  "ErrorCode": -4,
  "CustomErrorMessage": "An annotation on document poc_doc_name could not be saved."
}
HTTP

In other cases the response included the workspace name string inside the message, for example when saving annotations to some folders. The two screenshots Researcher captured (redacted) show the different DocumentArtifactID values and the returned document names that confirm the exposure.

Annotation save response showing document name in error message

400 Bad Request JSON exposing private document name


Step 4 - Automating enumeration safely (how an attacker would scale)

The vulnerability becomes high-impact when automated. An attacker with a valid user cookie can:

  1. Enumerate WorkspaceID values (using a safe wordlist or ranges).
  2. For each discovered workspace, iterate DocumentArtifactID values and submit the annotation payload above.
  3. Parse CustomErrorMessage strings for visible document/workspace names and store the results.

A pseudo-automation approach (ethical use only):

# PSEUDOCODE (do not run without permission)
for ws in workspace_list; do
  for docid in seq 1000000 1005000; do
    send_post "/workspaces/$ws/annotations/save" payload_with_docid
    if response contains "could not be saved"; then
      store(ws, docid, response.CustomErrorMessage)
    fi
  done
done
Bash

Because the API returned different HTTP codes and messages (500 vs 400) depending on the scenario, the enumeration script can use response status and body text to confirm existence and capture names.

Defensive note: rate limiting, strong access checks, and sanitized error content would stop this.

Flowchart-style diagram illustrating the automated enumeration process, showing an attacker brute-forcing WorkspaceIDs and iterating DocumentArtifactIDs to parse error responses for names, presented in a minimalistic cyber style.


Step 5 - Root cause analysis (why this leak occurs)

The root causes combine two developer-side mistakes:

  1. Weak authorization checks: the server attempts to process the annotation before performing an ownership check, leading to error paths that expose metadata.
  2. Verbose, unauthenticated error messages: CustomErrorMessage strings include human-friendly names for debugging or UX reasons, but these are not sanitized when returned to unauthorized callers.

When combined, an attacker can elicit the server into disclosing internal strings simply by triggering error conditions.

Diagram comparing insecure versus secure flows to illustrate the root causes of the information disclosure, highlighting verbose errors and late authorization checks in a clean, educational vector style.


Step 6 - Impact assessment

Even though the payloads do not directly download document contents, leaking document and workspace names can be sensitive:

Impact Why it matters
Confidentiality loss Document names may include client names, PII, case IDs
Reconnaissance Names help an attacker choose high-value targets for social engineering
Regulatory exposure PII leaks can lead to GDPR/other compliance concerns
Business risk Project or client names can reveal business relationships

Depending on how teams name documents (for example, including full client names, identifiers, or case numbers), the severity can range from moderate to high. In the reported case the program triaged and resolved the issue and the researcher received a bounty.


Step 7 - Immediate remediations (what to patch now)

If you maintain a document viewer API, prioritize the following fixes right away:

1. Sanitize error messages

Do not return human-friendly object names in error payloads visible to unauthorized users. Return generic error codes/messages and log details server-side.

2. Enforce ownership checks as early as possible

Before attempting to process or validate payloads that reference WorkspaceID or DocumentArtifactID, verify the caller’s authorization. Reject unauthorized access with a generic 403/404.

3. Use non-guessable identifiers where appropriate

If possible, adopt UUIDs or other non-sequential identifiers that make enumeration harder.

4. Rate limit and monitor enumeration patterns

Apply request throttling per logged-in user or IP and trigger alerts on high-volume enumeration behavior.

5. Do not use verbose debug strings in production

Developer-friendly messages belong in logs, not in client-facing responses.

6. Redaction & logging

If a server needs to include a context message for downstream systems, ensure that logs keep the sensitive name while the API response returns only a generic token the developer can correlate.

Infographic listing immediate remediation steps: sanitize errors, implement early ownership checks, apply rate-limiting, and use non-guessable IDs, each with an icon and a one-line description.


Step 8 - Longer-term hardening & testing

Beyond immediate fixes:

  • Periodic scanning: run automated scans looking for error-message leakage across APIs.
  • Threat modeling: consider naming conventions and what metadata is safe to expose.
  • Secure development training: teach devs the dangers of informative errors.
  • Response validation: ensure test suites assert that error bodies do not contain object names.
  • Incident playbooks: prepare processes for disclosure and rapid patching when leaks are found.

Troubleshooting & common pitfalls during remediation

Teams sometimes make mistakes while fixing this issue:

  • Fixing only the annotation endpoint but not other endpoints that also embed names in errors.
  • Returning generic HTTP codes but still embedding metadata inside nested JSON fields.
  • Overly broad 404s that break legitimate functionality - prefer 403 for unauthorized, 404 for not found depending on design.
  • Forgetting to remove verbose logging to client when moving code from staging to production.

A careful regression test across the entire DocumentViewer surface is essential.


Responsible disclosure & reward outcome

When responsibly disclosed to the vendor, the issue was triaged and patched. The program confirmed the root cause (error message leakage + insufficient authorization checks) and applied fixes across API layers. The researcher was rewarded with a bounty. That outcome - patching, acknowledgement, and reward - is how we keep systems safer.


Final thoughts

This IDOR variant is deceptively simple: it is not an SQLi or broken auth flow, but error-message leakage plus predictable IDs. Teams can prevent similar findings by sanitizing responses, performing ownership checks early, and making enumeration difficult via unpredictability and rate-limiting.

If you maintain APIs that reference business-sensitive objects, treat error content as sensitive too - never leak metadata to unauthorised callers.

Join the Security Intel.

Get weekly VAPT techniques, ethical hacking tools, and zero-day analysis delivered to your inbox.

Weekly Updates No Spam
Herish Chaniyara

Herish Chaniyara

Web Application Penetration Tester (VAPT) & Security Researcher. A Gold Microsoft Student Ambassador and PortSwigger Hall of Fame (#59) member dedicated to securing the web.

Read Next

View all posts

For any queries or professional discussions: herish.chaniyara@gmail.com