Sanitized finding from Client R. Payloads, domains and user identifiers are intentionally excluded.
Context
The application allowed users to upload documents that were later displayed to other users inside the main application origin.
Finding
The server-side filter could be bypassed with an active document format and content variations that did not match the expected inert-file policy. The resulting object was stored and rendered without sufficient isolation or sanitization.
Technical workflow
The test matrix varied only one property at a time: filename, detected type, declared MIME type and rendering context. A safe upload check can be exercised with an inert fixture:
curl --fail-with-body --max-time 10 \
-H 'Authorization: Bearer TEST_TOKEN' \
-F 'file=@fixtures/benign-document.pdf;type=application/pdf' \
'https://AUTHORIZED_HOST/api/documents'
The server-side policy should be allowlist-based and independent of browser metadata:
ALLOWED = {"application/pdf", "image/png", "image/jpeg"}
def upload_policy(*, detected_mime: str, size: int) -> None:
if detected_mime not in ALLOWED:
raise ValueError("active or unsupported format")
if size > 10 * 1024 * 1024:
raise ValueError("document exceeds size budget")
The rendered response must come from an isolated origin with restrictive headers. Validation included both ordinary users and privileged reviewers, because stored content is dangerous precisely when it crosses a trust boundary.
Impact
The execution context could be used to perform actions as a victim, access data exposed to the browser and target privileged users. The issue was therefore more serious than a self-XSS or isolated upload validation defect.
Remediation
- Enforce an inert-file allowlist using content inspection, not filename or browser-provided MIME type.
- Store and serve uploads from a separate origin with restrictive response headers.
- Sanitize content when a business requirement makes active formats unavoidable.
- Rotate sessions after privilege changes and deploy a restrictive CSP.
Validation
Verify that renamed, polyglot and content-type-confused files are rejected or rendered as inert downloads for every role.
Expected result: no inline active rendering, no session-bearing response to an uploaded document and no privileged action from a document view.