This weakness occurs when software accesses a file by name without checking whether that name points to a symbolic link, shortcut, or other indirect reference…
This weakness occurs when software accesses a file by name without checking whether that name points to a symbolic link, shortcut, or other indirect reference that could resolve to a different file than intended. An attacker can create a link with a trusted name that points to a sensitive file elsewhere on the system, tricking the application into reading or writing to the wrong location.
02How It Happens
Applications often need to read or write files based on user input or configuration. If the code simply opens a file by its path without verifying that the path is a direct file (not a link), an attacker who can create files or directories on the system can place a symbolic link or shortcut at that path. When the application follows the link, it may access a file outside the intended directory—such as system configuration files, private keys, or other users' data. This is especially dangerous in multi-user systems or shared hosting environments where an attacker can write to a temporary directory or a web-accessible folder.
03Real-World Impact
An attacker could read sensitive files (private keys, configuration with credentials, source code) by creating a link in a predictable location. Alternatively, if the application writes to the file, an attacker could redirect writes to overwrite critical system files or inject malicious content into files that are later executed. In shared hosting or containerized environments, this can lead to privilege escalation or lateral movement between users or tenants.
04Vulnerable & Fixed Patterns
Vulnerable pattern
import os
def process_user_file(filename):
# Directly open file without checking if it's a link
with open(filename, 'r') as f:
data = f.read()
return data
# Attacker creates: /tmp/user_data -> /etc/passwd
result = process_user_file('/tmp/user_data')
Why it's vulnerable: The code does not verify that /tmp/user_data is a regular file; if it is a symbolic link, open() will follow it and read whatever it points to, potentially exposing sensitive files.
Fixed pattern
import os
def process_user_file(filename):
# Verify the file is not a link and is in the intended directory
if os.path.islink(filename):
raise ValueError("File is a symbolic link; not allowed")
# Optionally: resolve and verify the real path is within expected directory
real_path = os.path.realpath(filename)
allowed_dir = os.path.realpath('/tmp/safe_uploads')
if not real_path.startswith(allowed_dir):
raise ValueError("File is outside allowed directory")
with open(filename, 'r') as f:
data = f.read()
return data
Vulnerable pattern
<?php
function process_user_file($filename) {
// Directly read file without checking for links
$data = file_get_contents($filename);
return $data;
}
// Attacker creates: /var/www/uploads/config -> /etc/passwd
$result = process_user_file('/var/www/uploads/config');
?>
Why it's vulnerable: file_get_contents() will follow symbolic links transparently, allowing an attacker to read any file the web server process has permission to access.
Fixed pattern
<?php
function process_user_file($filename) {
// Check if the file is a symbolic link
if (is_link($filename)) {
throw new Exception("File is a symbolic link; not allowed");
}
// Verify the real path is within the intended directory
$real_path = realpath($filename);
$allowed_dir = realpath('/var/www/uploads');
if ($real_path === false || strpos($real_path, $allowed_dir) !== 0) {
throw new Exception("File is outside allowed directory");
}
$data = file_get_contents($filename);
return $data;
}
?>
05Prevention Checklist
Check for links before access: Use is_link() (PHP) or os.path.islink() (Python) before opening files, and reject symbolic links if they are not explicitly expected.
Validate resolved paths: After resolving a path with realpath() or os.path.realpath(), verify that the real path is within the intended directory tree.
Use secure temporary directories: Create temporary files in directories with restrictive permissions (e.g., mode 0700) that only the application user can access.
Avoid predictable file paths: Do not use fixed or easily guessable paths for temporary or working files; use randomized names or secure APIs like tempfile.mkstemp() (Python) or tmpfile() (PHP).
Run with minimal privileges: Ensure the application process runs with the lowest necessary permissions so that even if a link is followed, the damage is limited.
Audit file operations: Log and monitor file access, especially to sensitive locations, to detect suspicious link-following behavior.
06Signs You May Already Be Affected
Check your application logs for unexpected file access patterns—reads from system directories like /etc/ or /var/ when the application should only touch its own directories. Look for symbolic links in your upload or temporary directories that point outside the intended location. If you have file integrity monitoring in place, watch for unexpected changes to critical files that the application should not modify.