SecureBlockLog inStart a pentest
Vulnerability Repository
HighAccess Control

IDOR via File Download

File download endpoints that use predictable or user-supplied identifiers without authorisation checks allow attackers to access any user's private files and documents.

CVSS 8.1CWE CWE-22OWASP A01:2021 — Broken Access Control

Description

IDOR (Insecure Direct Object Reference) via file download occurs when a file download endpoint accepts a file identifier — typically a filename, path, or sequential ID — supplied by the client and serves the corresponding file without verifying that the requesting user is authorised to access it. An attacker can enumerate or guess other users' file identifiers and download their private documents, invoices, medical records, or any other stored content.

CWE-22 — Improper Limitation of a Pathname to a Restricted Directory (Path Traversal) is the applicable weakness when the identifier is a filename or path that can be manipulated to escape the intended directory. In cases where the identifier is a sequential database ID or UUID, CWE-639 — Authorization Bypass Through User-Controlled Key is more precise, though both are frequently cited together in file download IDOR findings.

Under A01:2021 — Broken Access Control, the OWASP Top 10 lists IDOR as the most common and impactful access control failure. File download endpoints are a particularly high-value target because files often contain dense concentrations of sensitive data — a single invoice PDF or account statement can contain more PII than an entire API response, and the business impact of accessing another user's financial documents or medical records is immediately clear.

How It Works

Sequential ID-based IDOR:

# Authenticated user downloads their own invoice
GET /api/download/invoice?id=10482 HTTP/1.1
Cookie: session=eyJhbGc...

HTTP/1.1 200 OK
Content-Disposition: attachment; filename="invoice-10482.pdf"

The attacker increments the ID:

GET /api/download/invoice?id=10481 HTTP/1.1
GET /api/download/invoice?id=10480 HTTP/1.1

No authorisation check verifies that 10481 belongs to the requesting user. The attacker downloads invoices for every account in the system.

Filename-based path traversal IDOR:

# Application uses the submitted filename directly
GET /download?file=user_12345_report.pdf HTTP/1.1

# Attacker substitutes another user's filename
GET /download?file=user_99999_report.pdf HTTP/1.1

# Path traversal variant to escape the intended directory
GET /download?file=../../etc/passwd HTTP/1.1
GET /download?file=..%2F..%2Fetc%2Fpasswd HTTP/1.1
GET /download?file=....//....//etc/passwd HTTP/1.1

S3 pre-signed URL IDOR — some applications generate time-limited S3 pre-signed URLs for file downloads. If the URL generation does not enforce ownership, the URLs themselves can be shared or the underlying S3 key path can be guessed:

GET /api/files/presign?key=uploads/user_10481/contract.pdf HTTP/1.1
→ 200: {"url": "https://bucket.s3.amazonaws.com/uploads/user_10481/contract.pdf?X-Amz-Signature=..."}

Substituting user_10480 in the key parameter generates a valid pre-signed URL for another user's file if no ownership check is performed.

Automated enumeration with ffuf:

ffuf -u 'https://app.example.com/api/download?id=FUZZ' \
     -w <(seq 10000 11000) \
     -H 'Cookie: session=<your_session>' \
     -fc 403,404 \
     -o results.json

Impact

  • Mass data exfiltration — sequential IDs allow automated download of all files for all users in the system.
  • Privacy violation — access to invoices, medical records, identity documents, and contracts belonging to other users constitutes a serious privacy breach under GDPR and HIPAA.
  • Business espionage — in B2B platforms, accessing other organisations' contracts, proposals, or financial statements constitutes corporate espionage.
  • Compliance breach — unauthorised access to healthcare or financial documents triggers mandatory regulatory notification.
  • Path traversal escalation — when the file identifier is a path, IDOR can escalate to full server-side file system access, including application configuration and system files.

Detection

  1. Create two test accounts and cross-reference file identifiers — as User A, upload or generate a file and note its identifier. As User B, attempt to download that file using User A's identifier.
  2. Enumerate sequential IDs — if file IDs appear to be sequential integers, use Burp Intruder with a numeric payload sequence to test a range of IDs below and above your own.
  3. Test path traversal sequences — if the parameter contains a filename or path, test ../, ..%2F, ..%252F, ....//, and Unicode equivalents as path components.
  4. Test for missing authentication — attempt to access file download URLs with no session cookie or with an invalid session token.
  5. Check S3 or cloud storage key patterns — if downloads involve cloud storage, inspect the URL or the API parameter for user-identifiable path components and attempt substitution with other user identifiers.

Remediation

Enforce authorisation at the file record level. Every file download request must verify that the authenticated user's account is the owner of (or has been explicitly granted access to) the requested file. This check must occur at the application layer, not at the storage layer:

def download_file(request, file_id):
    file = File.objects.get(id=file_id)
    if file.owner != request.user:
        return HttpResponse(status=403)
    return serve_file(file.path)

Never use user-supplied paths directly for file system access. Resolve file paths exclusively from database records associated with authenticated users. Never concatenate user input into file system paths:

# Vulnerable
path = os.path.join(UPLOAD_DIR, request.GET['filename'])

# Secure — resolve from DB record owned by current user
file_record = UserFile.objects.get(id=file_id, owner=request.user)
path = os.path.join(UPLOAD_DIR, file_record.storage_key)

Use opaque, non-guessable storage keys. Store files using randomly generated UUIDs as keys rather than user IDs or sequential numbers — this adds an enumeration barrier on top of the required authorisation check.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping