CWE-35: Path Equivalence
Overview
Path equivalence vulnerabilities occur when different representations of a file or directory path are treated as equivalent by the operating system but not by the application, allowing attackers to bypass access controls or validation.
Risk
Medium: Attackers may access restricted files or directories by exploiting alternate path representations, leading to unauthorized access or data exposure.
Relationship to Other CWEs
CWE-35 vs CWE-22 vs CWE-73:
- CWE-35 (This page): Focuses on equivalent path representations that bypass validation (e.g.,
/app/filesvs/app/files/vs/app//filesvs/app/files/.). This occurs when security checks use string comparison without canonicalization. Relatively uncommon in scan findings.
- CWE-22 (Path Traversal): Focuses on escaping directories using
../sequences. Much more common. If your finding involves traversal sequences, refer to CWE-22 guidance instead.
- CWE-73 (External Control of File Name or Path): Broader category covering any user control over paths. If you have CWE-73, determine whether the issue is traversal (CWE-22) or path comparison (CWE-35) or both.
When to use CWE-35 guidance: Apply this when your code compares paths for access control (e.g., "does requested path equal allowed path?") without first normalizing both to canonical form. The fix is to always canonicalize before comparison.
Remediation overlap: All three require canonicalization. CWE-35 particularly emphasizes proper path comparison logic.
Remediation Steps
Core principle: Never allow untrusted input to specify absolute or root-anchored paths; all filesystem access must be resolved relative to a server-controlled base directory and verified for canonical containment.
Trace the Data Flow from Untrusted Source to Path Operation
Review scan results:
- Check scan results: Review the specific file path, line number, and variable where user input is used in path operations
- Trace data flow: Follow the path from source (HTTP parameters, query strings, form data, file upload names, API inputs) to sink (file open, path comparison, directory listing, file deletion)
- Identify path operations: Locate where paths are used (file access, path validation, access control checks, symbolic link resolution)
- Map path representations: Identify all places where the same logical path might be represented differently
Find path equivalence issues proactively (good developer habits):
During development:
- Identify all file operations: Map every place your code accesses files using external input (file downloads, uploads, includes, template loading, log file access)
- Question path comparisons: Any time you compare paths for access control, ask "Could these be represented differently?"
- Test with equivalents: When writing file access code, test with
/path,/path/,/path/.,//pathto verify normalization - Assume malicious paths: Treat all external path input as potentially containing traversal sequences or encoding tricks
Code review checklist:
- Check for path normalization: Is
realpath(),os.path.realpath(), orPath.GetFullPath()called before validation? - Verify prefix checking: After normalization, is
startswith()or equivalent used to verify allowed directory? - Look for string comparisons: Flag any path equality checks using
==orstrcmp()without normalization first - Review symbolic link handling: Does code resolve symlinks? Could symlinks bypass directory restrictions?
- Check for double normalization: Is path normalized once before all uses, or re-normalized multiple times (risk of TOCTOU)?
Search your codebase for vulnerable patterns:
- File operations with user input:
open(request.params,File(user_input),readFile(req.query - Path comparisons without normalization:
if path == allowed_path,path.equals(allowlist) - String operations on paths:
path.replace(),path.split(), string concatenation for path construction - Relative path usage:
../,./, paths starting with.. - Path validation with regex only (no canonicalization)
Use development tools:
- Static analysis: Use tools that detect path traversal (Semgrep rules for file operations, CodeQL queries)
- IDE warnings: Configure IDEs to warn on file operations with non-constant paths
- Linters: Add custom lint rules to flag file operations without normalization
- Path validation libraries: Use established libraries (pathlib in Python, Path API in Java, path module in Node.js)
- Security unit tests: Write tests that verify path equivalence handling (various representations of same path)
Architectural best practices:
- Abstraction layer: Create a secure file access class that handles normalization and validation centrally
- Allowlist configuration: Store permitted directories in config, never derive from user input
- Principle of least privilege: Run file operations in sandboxed directories with minimal permissions
- Avoid direct file access: Use database-backed file metadata with generated IDs instead of user-provided filenames
Normalize All Paths Before Validation or Comparison
- Use OS-appropriate canonicalization functions (
realpath(),Path.GetFullPath(),os.path.realpath()) - Resolve all symbolic links,
., and..components - Convert to absolute paths with consistent separators
- Normalize character encoding (handle Unicode normalization)
- Handle case sensitivity appropriately for the OS (case-insensitive on Windows)
- Apply normalization BEFORE any security checks or comparisons
Use Strict Allowlisting for Permitted Paths
- Define a base directory or set of allowed directories
- After normalization, verify the canonical path starts with an allowed base path
- Use exact prefix matching (not substring or pattern matching)
- Reject any path outside the allowed directory tree
- Store allowlists in configuration, not derived from untrusted data
- Validate that resolved paths remain within expected boundaries
Eliminate Ambiguous Path Representations
- Block paths containing
..before normalization (defense in depth) - Reject paths with encoded traversal sequences (
%2e%2e%2f,..%252f) - Deny access via symbolic links that point outside allowed directories
- Block unusual encodings: URL encoding, Unicode variations, null bytes
- Prevent double-encoding attacks by applying single-pass decoding
Add Access Controls and Monitoring
- Restrict file operations to specific directories using OS-level permissions
- Run processes with least privilege (minimal file system access)
- Log all file access attempts with both original and normalized paths
- Alert on path traversal attempts or access outside allowed directories
- Monitor for repeated canonicalization failures or access denials
- Regularly audit file access logs for suspicious patterns
Test and Verify the Fix
- Test with the specific input from the security finding (should be blocked or normalized correctly)
- Test equivalent path representations:
/app/data,/app//data,/app/./data,/app/data/. - Test symbolic link bypasses (create symlink outside allowed dir)
- Test case variations on case-insensitive systems:
FILE.txtvsfile.txt - Test encoded paths:
%2fapp%2fdata, Unicode variations - Test path traversal:
allowed/../../etc/passwd - Verify legitimate file access with various valid path formats works correctly
- Re-scan with the security scanner to confirm the issue is resolved
- Check for any new findings introduced by the changes
Language-Specific Guidance
For detailed code examples and patterns specific to your programming language, see:
- Python - pathlib.Path.resolve(), is_relative_to(), Unicode normalization
- Java - Path.toRealPath(), Files.isRegularFile(), symlink resolution
- JavaScript/Node.js - path.resolve(), path.relative(), double-encoding protection
- C# - Path.GetFullPath(), ordinal comparison, case sensitivity handling
- PHP - realpath(), prefix validation, directory separator handling
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code