Lukas' Notes

web security

Definition

Canonical Path

The canonical path of a filesystem path is the absolute, normalised path with .., ., and symbolic-link segments fully resolved. realpath($p) returns it.

To confine dynamic file inclusion to a directory, compute the canonical path of the user-supplied input prefixed with the directory, then check that the result still starts with the target directory prefix:

$pdir  = '/var/www/htdocs/pages/';
$fname = realpath($pdir . $_GET['file']);
if (str_starts_with($fname, $pdir)) { /* safe to read */ }

This defeats path traversal because any ../ is resolved away before the prefix test, so an escaped path no longer begins with $pdir.

Example

Blocking a ../ escape

Let the webroot pages directory be the prefix

An attacker requests ?file=../../../etc/passwd, so the application builds

Naively checking that this string starts with pdir would pass — every prefix character matches — and yet the path clearly escapes the directory. The canonical-path defence resolves it before testing:

prefix/var/www/htdocs/pages/
realpath()/etc/passwd
starts-with check/etc/passwd starts with /var/www/htdocs/pages/? no

Since the resolved path no longer begins with pdir, the check rejects the read. A benign request like ?file=about.php resolves to /var/www/htdocs/pages/about.php, which does start with pdir, so it is allowed.

The point is that realpath() collapses the ../ sequence and resolves any symlinks before the prefix test runs, so the comparison answers the question that matters — “did we land back inside the directory?” — rather than whether the raw user string happened to begin with the right characters.