A file path is a string that indicates how to find a file, starting from a particular directory. If a path begins with the root directory or with a root volume (e.g., C: in Windows), it is an absolute path; otherwise, it is a relative path.

Absolute or relative path names may contain file links such as symbolic (soft) links, hard links, shortcuts, shadows, aliases, and junctions. These file links must be fully resolved before any file validation operations are performed. For example, the final target of a symbolic link called trace might be the path name /home/system/trace. Path names may also contain special file names that make validation difficult:

  • "." refers to the directory itself.
  • Inside a directory, the special file name ".." refers to the directory's parent directory.

In addition to these specific issues, a wide variety of operating system–specific and file system–specific naming conventions make validation difficult.

The process of canonicalizing file names makes it easier to validate a path name. More than one path name can refer to a single directory or file. Furthermore, the textual representation of a path name may yield little or no information regarding the directory or file to which it refers. Consequently, all path names must be fully resolved or canonicalized before validation. Because the canonical form can vary among operating systems and file systems, it is best to use operating system–specific mechanisms for canonicalization; however, this option is often not available.

For example, validation may be necessary when attempting to restrict user access to files within a particular directory or otherwise make security decisions based on a file name or path name. Frequently, an attacker can circumvent these restrictions by exploiting a directory traversal or path equivalence vulnerability. A directory traversal vulnerability allows an I/O operation to escape a specified operating directory. A path equivalence vulnerability occurs when an attacker provides a different but equivalent name for a resource to bypass security checks.

Canonicalization contains an inherent race window between the time the program obtains the canonical path name and the time it opens the file. While the canonical path name is being validated, the file system may have been modified and the canonical path name may no longer reference the original valid file. Fortunately, this race condition can be easily mitigated. A path name that is a secure path is immune to race windows and other attempts by an untrusted user to confuse the program. See FIO01-PL. Do not operate on files that can be modified by untrusted users for more information on secure paths.

Noncompliant Code Example (POSIX)

This noncompliant code example allows the user to specify a file inside the /img directory for the program to work with. Because of its lack of checks, the user can specify files outside the intended directory by entering an argument that contains ../ sequences and consequently violates the intended security policies of the program.

sub work_with_image {
  my ($image_file) = @_; # untrusted
  open( my $image, "<", "/img/$image_file") or croak "Can't open image file";
  # ...
}

Noncompliant Code Example (POSIX, File::PathConvert)

This noncompliant code example attempts to enforce that the file specified still lives within the /img directory. However, it is using the File::PathConvert module, which has been deprecated.

use File::PathConvert qw(realpath $resolved);

sub work_with_image {
  my ($image_file) = @_; # untrusted
  $image_file = realpath("/img/$image_file") || croak "Resolution stopped at $resolved";
  if ($image_file !~ m|/img/|) {
    croak "Image file not in /img";
  }
  open( my $image, "<", $image_file) or croak "Can't open $image_file";
  # ...
}

According to the CPAN entry for File::PathConvert:

There are several known bugs, and it is not being actively
maintained since all functionality is now available in
modules (Cwd.pm and File::Spec) bundled in every Perl
distribution of recent vintage. This version is provided to
fix a few bugs and to get the word out about the
deprecation.

Compliant Solution (POSIX, Cwd)

This compliant solution obtains the file name from the untrusted user input and canonicalizes it using Perl's Cwd module, which is part of the standard Perl distribution.

use Cwd 'abs_path';

sub work_with_image {
  my ($image_file) = @_; # untrusted
  $image_file = abs_path("/img/$image_file");
  if ($image_file !~ m|/img/|) {
    croak "Image file not in /img";
  }
  open( my $image, "<", $image_file) or croak "Can't open $image_file";
  # ...
}

Noncompliant Code Example

This noncompliant code example accepts a file path as a command-line argument and uses the is_secure_path() subroutine defined in FIO01-PL. Do not operate on files that can be modified by untrusted users. This ensures that the file is in a secure directory. The validate_path() routine performs string-based validation on the path name. This could include checking for such things as that

  • the file lives in the user's home directory.
  • the file ends with the proper suffix, such as .html.
  • the file does not contain "weird" characters such as spaces.
my $filename = $ENV{"HOME"} . $DIR_SEP . $ARGV[0];
# $DIR_SEP = / on POSIX or \ on Windows
croak "Not a secure path" if !is_secure_path( $filename);
croak "Invalid path" if !validate_path( $filename);

However, this code neither resolves file links nor eliminates equivalence errors. Consequently, the validation routine may pass on the path name given, whereas the path name might resolve to a file that the validation routine would fail on. For instance, a path name that starts with /home/person might resolve to a file that lives outside /home/person, foiling a validation routine that ensures that the file lives in the person's home directory.

Compliant Solution (POSIX)

This compliant solution uses the Cwd module to obtain the file's canonical path before performing any validation. This guarantees that any string-based operations the validation may perform on the path are performed on the canonical path and therefore cannot be foiled by symbolic links or . or .. in the path.

Furthermore, canonicalization is performed after the file has been verified to live in a secure path. This prevents attackers from conducting time-of-check, time-of-use (TOCTOU) attacks against the program during the abs_path() call, or the validation, or any subsequent operations on the path.

use Cwd 'abs_path';

my $DIR_SEP = "/";

my $filename = $ENV{"HOME"} . $DIR_SEP . $ARGV[0];
croak "Not a secure path" if !is_secure_path( $filename);
$filename = abs_path( $filename);
croak "Invalid path" if !validate_path( $filename);

Compliant Solution (Windows)

Producing canonical file names for Windows operating systems is extremely complex and beyond the scope of this standard. The best advice is to try to avoid making decisions on the basis of a path, directory, or file name [Howard 2002]. Alternatively, use operating system–based mechanisms, such as access control lists (ACLs) or other authorization techniques.

Risk Assessment

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

IDS00-PL

medium

unlikely

medium

P4

L3

Automated Detection

Tool

Diagnostic

Notes
Taint mode

Insecure dependency in .*open

Detects only files open for writing.
Does not detect files open only for reading.

Related Guidelines

Bibliography

 


2 Comments

  1. Anonymous

    The first "Compliant Solution" still allows which uses abs_path is still vulnerable, because it doesn't check whether the returned path does being with /img.