Skip to content

CWE-73: External Control of File Name or Path

Overview

User input reaches a file or directory name, so the request rather than the application decides which file gets read, written, or deleted.

Relationship to Other CWEs

CWE-73 vs CWE-22 vs CWE-41:

These are related weaknesses that commonly chain, not a parent and two subcategories. MITRE places CWE-73 under CWE-610 (Externally Controlled Reference to a Resource in Another Sphere) and records it as CanPrecede CWE-22 and CWE-41, which sit under a different parent in the tree. External control of the path is what makes traversal or equivalence attacks possible, but each is a distinct weakness with its own fix.

  • CWE-73 (this page) - Untrusted input reaches a file name or path. That covers arbitrary file selection inside an allowed directory as well as escapes from it, so a finding can be CWE-73 with no traversal sequence anywhere in the payload.
  • CWE-22 (Path Traversal) - The path resolves outside the intended directory, typically through ../ sequences. If the finding shows traversal patterns, apply the CWE-22 guidance as well.
  • CWE-41 (Improper Resolution of Path Equivalence) - Two different spellings of the same path are treated as different by validation and the same by the filesystem - trailing dots, doubled separators, 8.3 short names. Relevant when validation compares path strings without canonicalizing them first.

Which guidance to follow:

  • Traversal patterns present (../, encoded dots): apply both CWE-73 and CWE-22 guidance.
  • No traversal: focus on indirect references and allowlisting from the Remediation Steps.
  • Uncertain: follow all CWE-73 remediation steps - they cover the common cases.

Remediation overlap: Indirect references and canonicalization apply to all three CWEs.

OWASP Classification

A06:2025 - Insecure Design

Risk

High: An attacker who controls the path chooses which file the application reads, overwrites, or deletes, including files outside the directory the feature was meant to expose.

Remediation Steps

Core Principle: Never let untrusted input choose file names, paths, or file operations; map external identifiers to server-controlled filenames and enforce canonical containment and safe file semantics.

Trace the Data Flow from Untrusted Source to File Operation

  • Start from the finding: the file, line number, and code pattern it points at
  • Trace how untrusted data (user input, external files, databases, network requests) reaches the file operation
  • Note every point along the way where the file path is constructed or modified

Eliminate Direct Use of Untrusted Data in File Paths

  • Never use untrusted data directly as a file name or path component
  • Replace direct path construction with indirect references: map the user's selection to an internal identifier such as a numeric ID
  • Use allowlists that map those identifiers to actual file paths
  • Store the mapping server-side where it cannot be manipulated

Use Strict Validation and Canonicalization

  • Canonicalize all file paths before validation using OS-appropriate functions
  • Validate that the canonicalized path stays within allowed directories
  • Reject absolute paths, symbolic links, null bytes, and traversal sequences in any encoding, including .. and its percent-encoded forms
  • Use allowlists of permitted file names or patterns rather than denylists
  • Validate file extensions against expected types

Add Directory Restriction and Access Controls

  • Configure a base directory for all file operations
  • Verify that all resolved paths remain within the base directory
  • Use chroot jails or similar OS mechanisms to restrict file access scope
  • Run the process with the minimum file system permissions it needs
  • Keep the files read-only where the feature only needs to read them

Apply Multiple Layers of Defense

  • Use indirect references and path validation together, not one or the other
  • Add runtime monitoring and logging for all file access operations
  • Log suspicious patterns (traversal attempts, access denials)
  • Set up alerts for repeated access violations
  • Use security frameworks or libraries that provide built-in path validation

Test and Verify the Fix

  • Test with the specific input from the security finding (should be blocked)
  • Test path traversal attempts: ../../etc/passwd, ..\..\windows\system32\config\sam
  • Test encoded traversal: %2e%2e%2f, ..%252f, ..%c0%af
  • Test absolute paths: /etc/passwd, C:\Windows\System32
  • Test null bytes: allowed.txt%00.jpg, file.txt\0
  • Verify legitimate file access still works correctly
  • Re-scan to confirm the finding is resolved and that the fix introduced no new ones

Common Vulnerable Patterns

  • Directly using user input in file operations
  • Allowing directory traversal via ../ or encoded characters

Unsanitized Filename from User Input (Pseudocode)

// VULNERABLE - Unsanitized Filename from User Input
# Uses user input for file access
open(request.args['filename'])

Why this is vulnerable: The path handed to the read comes from a value the request controls, and nothing in between checks that the result is still inside base_dir. The sketch is language-neutral because the flaw is the missing containment check, not the string operation that builds the path. The language pages show what the join and the check look like in each ecosystem.

Secure Patterns

Allowlist-Based File Access (Pseudocode)

# Use allowlist and validate path
allowed_files = {'report.txt', 'summary.txt', 'data.csv'}
filename = request.args['filename']
if filename in allowed_files:
    open(filename)
else:
    raise Exception('Invalid file')

Why this works:

  • User input selects an entry in a server-controlled allowlist rather than becoming a filesystem path, so only pre-approved files can be read or written
  • Traversal sequences such as ../, ..\, or encoded variants never match an allowlist entry, so /etc/passwd and other files outside the intended directory stay out of reach
  • Canonicalization and an authorization check on top of the allowlist keep access inside the base directory

Common Pitfalls

  • Allowlisting the file extension but not the path: Checking that a supplied filename ends in an approved extension such as .pdf before opening it - The extension check says nothing about the directory portion of the value; ../../secrets/config.pdf or an absolute path ending in the same extension still passes and can reach files well outside the intended directory.
  • Validating a sanitized copy while opening the original value: Deriving a "clean" filename, for example by stripping directory components, only to check it against a denylist or allowlist, then passing the original user input to the file operation - The value that was inspected and the value that gets opened are two different strings, so a rejected pattern in the sanitized copy does not stop the same pattern from reaching the filesystem through the untouched original.
  • Treating file existence as authorization: Using File.Exists(), os.path.isfile(), or an equivalent check as the gate for whether a request is allowed - Confirming that something exists at an attacker-influenced path is not the same as confirming the caller is allowed to read it; a path traversal or arbitrary file-selection bug still lets the attacker pick which existing file gets served.
  • String-prefix containment without canonicalizing first: Comparing a resolved path to the base directory with startsWith() before symlinks and ../. segments have been fully resolved - An unresolved path can still contain traversal sequences or point through a symlink, so the prefix comparison passes on a string that has not actually been confirmed to live inside the base directory.

Language-Specific Guidance

  • C# - Path.GetFileName, Path.Combine, allowlist validation
  • Java - Path.normalize, Files API, canonical path checks
  • Python - pathlib.Path.resolve, os.path.normpath, path traversal prevention

Additional Resources