Opens in a new tab
  1. Home
  2. Guides
  3. System
System guide

Path traversal: what it is and how plugins prevent it

Learn how path traversal can let WordPress plugins escape trusted directories and how canonical paths, realpath() and root containment help prevent it.

  • Updated September 4, 2026
  • 17 min read
  • WordPress guide

Path traversal is a file-system security vulnerability that occurs when an application lets untrusted input influence a file path without properly verifying where that path ultimately points.

In a WordPress plugin, a path traversal vulnerability can allow a user to escape the directory the plugin intended to expose and reach files elsewhere on the server.

A classic example is a path containing parent-directory references such as:

../../

If a file manager expects users to access only files inside:

/var/www/example/wp-content/uploads/

but accepts an unvalidated path such as:

../../wp-config.php

the resulting filesystem operation may point outside the uploads directory.

Depending on what the vulnerable feature can do, path traversal can potentially expose operations such as:

  • reading sensitive files;
  • downloading files outside the intended directory;
  • overwriting application files;
  • deleting files;
  • renaming files;
  • extracting archives into unintended locations;
  • creating files outside an approved storage area.

The vulnerability is not specific to WordPress. It is a general filesystem-security problem that can appear anywhere user-controlled data is turned into a path.

WordPress plugins are particularly relevant because file managers, backup systems, importers, upload tools and media utilities frequently need to manipulate files on behalf of administrators.

The solution is not simply to search a string for ../ and remove it.

Secure path handling requires understanding the difference between a path as text and the actual filesystem location that path resolves to.

This guide explains how path traversal works, why naive protections fail, how canonical paths and PHP’s realpath() help, how symbolic links complicate the problem and how WordPress plugins can restrict filesystem operations to approved directories.

What is path traversal?

A filesystem path identifies the location of a file or directory.

For example:

/var/www/example/wp-content/uploads/photo.jpg

is an absolute path on a typical Linux server.

A relative path instead describes a location in relation to another directory:

images/photo.jpg

Applications frequently combine a trusted base directory with an input value.

Conceptually:

$base_directory + $user_path

This becomes dangerous when the application assumes that the resulting string must remain below the base directory.

Parent-directory references change the destination

On Unix-like filesystems:

  • . refers to the current directory;
  • .. refers to the parent directory.

Suppose the approved directory is:

/var/www/example/wp-content/uploads/

and the application appends:

2026/photo.jpg

The result stays inside the approved area.

But if untrusted input contains:

../../wp-config.php

the filesystem resolves the parent-directory components rather than treating them as decorative characters.

The final destination can therefore escape the directory the developer intended to expose.

Path traversal is also called directory traversal

The terms path traversal and directory traversal are frequently used for the same vulnerability class.

OWASP describes path traversal as a technique that uses special path sequences to access files and directories outside the intended application directory.

Its Path Traversal documentation discusses common traversal patterns, encoded variants and the importance of validating filesystem access rather than trusting user-controlled path strings.

How a WordPress plugin can become vulnerable

Imagine a plugin provides an administrator-facing file viewer.

The intended root is:

wp-content/uploads/

A request may contain a value such as:

?file=2026/09/report.pdf

A vulnerable implementation could append that value directly to the uploads directory and read the result.

The developer may assume that because the interface only displays files from uploads, users can only request files from uploads.

But the server receives HTTP requests, not good intentions.

The interface does not define the security boundary

A dropdown containing only approved filenames does not prevent someone from manually sending a different value.

The same applies to:

  • hidden form fields;
  • JavaScript-generated requests;
  • REST API parameters;
  • AJAX requests;
  • query-string parameters;
  • JSON request bodies.

The official WordPress Security documentation states one of the central rules of plugin development clearly: data should not be trusted simply because it came from an expected interface.

Authorization and validation must happen on the server.

Administrators are not a reason to skip validation

A file manager may be restricted to administrators, which is an important authorization control.

But that does not make path validation unnecessary.

Requests can still be:

  • malformed accidentally;
  • modified intentionally;
  • generated by compromised accounts;
  • affected by another vulnerability;
  • sent through a compromised browser session.

WordPress’s data sanitization guidance specifically notes that even privileged users should not cause an application to trust arbitrary input blindly.

Why blocking ../ is not enough

The most obvious traversal pattern is:

../

That makes a simple defense tempting:

Search the incoming string for ../ and reject it.

This can be useful as an additional early check, but it is not a complete security boundary.

Paths can have multiple textual representations

Equivalent or related paths may contain:

  • current-directory segments;
  • duplicate separators;
  • parent-directory segments;
  • platform-specific separators;
  • symbolic links;
  • encoded input that is decoded before filesystem use.

A security rule based only on one exact substring is therefore fragile.

Normalization can happen after your validation

A particularly dangerous pattern is:

  1. validate the raw string;
  2. decode or transform it;
  3. pass the transformed value to the filesystem.

The object being checked is then not necessarily the object being used.

A safer principle is:

Validate the normalized filesystem destination that the application is actually going to operate on.

Sanitization and authorization are different

Removing suspicious characters does not answer the central security question:

Is this final filesystem location inside the area this operation is allowed to access?

The official WordPress data validation documentation distinguishes validation from generic sanitization and recommends checking data against the specific requirements of the application.

Canonical paths and realpath()

A strong defense begins by converting paths into a canonical representation before comparing them.

In PHP, one important function for existing filesystem targets is:

realpath()

The official PHP realpath() documentation explains that the function returns a canonicalized absolute pathname.

Among other things, it resolves:

  • /./ segments;
  • /../ segments;
  • extra directory separators;
  • symbolic links where supported.

Why canonicalization matters

Suppose the plugin receives:

/var/www/site/wp-content/uploads/2026/../private/file.txt

Comparing that raw string manually is unnecessarily complicated.

The filesystem ultimately cares about the resolved destination.

After canonicalization, that path may become:

/var/www/site/wp-content/uploads/private/file.txt

Now the plugin can compare a normalized destination with a normalized allowed root.

realpath() does not make a path safe by itself

This distinction is critical.

Calling:

realpath( $path )

does not automatically authorize the result.

For example, PHP documents that a traversal path can legitimately resolve to something like:

/etc/passwd

if that is where the supplied path points.

realpath() tells you the actual destination.

Your application still has to decide whether that destination is permitted.

How root containment checks prevent traversal

Once both paths are canonical, a plugin can verify that the requested target remains inside a trusted root.

Conceptually:

trusted root:
 /var/www/site/wp-content/uploads/

requested path:
 /var/www/site/wp-content/uploads/2026/photo.jpg

result:
 allowed

Compare that with:

trusted root:
 /var/www/site/wp-content/uploads/

resolved requested path:
 /var/www/site/wp-config.php

result:
 rejected

Canonicalize the root too

The approved directory should itself be resolved rather than assuming its textual representation is canonical.

A defensive implementation for an existing target may follow this general structure:

$root = realpath( $trusted_directory );
$path = realpath( $requested_path );

if ( false === $root || false === $path ) {
    // Reject.
}

$root = rtrim( $root, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;

if ( ! str_starts_with( $path . DIRECTORY_SEPARATOR, $root ) ) {
    // Reject: target escaped the trusted root.
}

The exact implementation should account for whether the target is a file or directory and for platform behavior, but the principle is more important than the particular snippet:

Resolve first, then verify containment.

A naive prefix comparison can also be wrong

Suppose your trusted directory is:

/var/www/uploads

and a requested path resolves to:

/var/www/uploads-malicious/file.txt

A careless check using only:

str_starts_with( $path, '/var/www/uploads' )

would return true.

But:

uploads

and:

uploads-malicious

are different directories.

Boundary-aware comparisons must include the directory separator or otherwise verify path components correctly.

Symbolic links make path validation more important

A symbolic link, or symlink, is a filesystem object that points to another location.

For example, something inside an approved directory could point to a location outside it.

Textually:

/var/www/site/wp-content/uploads/shared

might appear to live under uploads.

But if shared is a symbolic link, its actual destination could be elsewhere.

String checks cannot see through symlinks

A test that checks only whether the raw path begins with:

/var/www/site/wp-content/uploads/

cannot determine what a symbolic link ultimately targets.

This is another reason canonicalization matters.

PHP’s realpath() documentation states that the function expands symbolic links when resolving canonical filesystem paths, subject to platform limitations.

Symlink policy should be intentional

Some deployments legitimately use symbolic links for:

  • release directories;
  • shared uploads;
  • shared configuration;
  • deployment systems;
  • persistent storage.

A plugin therefore should not blindly assume that all symlinks are malicious.

Instead, its security rule should define the set of resolved filesystem locations the operation is allowed to access.

The important limitation of realpath(): new files do not exist yet

realpath() is extremely useful, but it has an important limitation.

The PHP documentation states that it returns false when the target does not exist.

This matters for operations such as:

  • creating a new file;
  • creating a directory;
  • uploading a file;
  • extracting a new archive entry;
  • renaming to a destination that does not yet exist.

You cannot canonicalize a nonexistent child in the same way

Suppose a plugin wants to create:

/trusted/root/new-folder/new-file.txt

If new-file.txt does not yet exist, calling realpath() on the complete destination will fail.

A safer strategy is to validate the existing parent directory and separately validate the new filename or path component.

Validate the parent before creating a file

A creation workflow can conceptually perform:

  1. separate the destination into parent directory and new basename;
  2. resolve the existing parent with realpath();
  3. verify that the resolved parent is inside the allowed root;
  4. validate the new basename;
  5. construct the final destination;
  6. perform the filesystem operation.

This prevents the developer from using realpath() as a magical incantation on something that, inconveniently, does not exist.

Never allow a filename to become a path accidentally

If an input is supposed to represent only a filename, it should not be allowed to introduce arbitrary directory components.

WordPress provides sanitize_file_name() for cleaning filenames.

Its documentation explicitly notes, however, that sanitizing a filename does not guarantee that the resulting file is permitted for upload.

Likewise, filename sanitization alone should not be treated as complete path containment.

WordPress functions that help with safer paths

WordPress provides several APIs that reduce the need to manually build filesystem locations.

Use WordPress directory functions instead of hardcoded paths

The official WordPress Plugin Handbook recommends using WordPress APIs to determine plugin and content directories instead of assuming that every site uses the default directory structure.

That matters because WordPress installations can:

  • move wp-content;
  • rename content directories;
  • use symlinks;
  • customize upload locations;
  • run through unusual deployment architectures.

A plugin should not assume that:

/var/www/html/wp-content/plugins/

is universally correct.

validate_file() detects suspicious relative paths

WordPress also provides:

validate_file()

for validating certain file path strings.

The WordPress data-validation handbook references this function for checking file paths.

It can detect conditions including traversal sequences and absolute paths that are not permitted by the intended calling context.

However, it should not be confused with a complete authorization system for arbitrary filesystem paths.

For a file manager operating on real server paths, resolving and verifying the final location remains important.

Use the WordPress Filesystem API when appropriate

Plugins that modify WordPress files should also understand the WordPress Filesystem API.

The API abstracts filesystem operations across hosting environments and supports direct, FTP and SSH-backed access depending on server configuration.

It does not eliminate the need to validate untrusted paths, but it helps plugins perform legitimate file operations using WordPress’s established filesystem layer.

For the related ownership and write-access model, see WordPress File Permissions Explained.

Path traversal can affect more than file reading

Many path traversal explanations focus on reading sensitive files.

That is only one possible outcome.

Arbitrary file download

A download endpoint may accept:

?file=manual.pdf

and return the selected file.

If the path is not constrained, the endpoint may expose files outside the intended downloads directory.

Arbitrary file deletion

A plugin may provide an AJAX endpoint that deletes a selected backup or media file.

If its path is vulnerable to traversal, a malicious request may try to target unrelated files.

The impact can become more serious because the operation modifies the filesystem rather than merely reading it.

Arbitrary file overwrite

An editor or save endpoint can be even more dangerous.

If an attacker can escape the permitted directory and overwrite:

  • PHP files;
  • configuration;
  • server directives;
  • theme files;
  • plugin files;

the result can escalate well beyond file disclosure.

Archive extraction

Archive extraction introduces a related vulnerability commonly called Zip Slip.

An archive can contain entries whose names include traversal sequences.

A vulnerable extractor may combine the destination directory with the archive entry name and write the result without checking the resolved location.

Every extracted entry therefore needs its own destination containment validation.

Path traversal and authorization solve different problems

A secure plugin should answer two separate questions before touching a file.

Question 1: may this user perform this operation?

WordPress capabilities answer application-level authorization questions.

For example:

current_user_can( 'manage_options' )

or a more specific capability appropriate to the feature.

Authorization determines whether the current user is allowed to use the feature.

Question 2: may the feature access this filesystem location?

Path validation answers a completely different question.

An administrator who is allowed to manage uploads should not automatically gain access to:

/etc/
another hosting account
SSH keys
server configuration
unrelated system files

unless the feature was explicitly designed to provide that access.

Nonces are not path validation either

A WordPress nonce can help protect an action against cross-site request forgery.

It does not make a malicious path safe.

A request can simultaneously contain:

  • a valid nonce;
  • a legitimate authenticated user;
  • an unauthorized filesystem path.

A robust endpoint needs authorization, request-integrity controls and path validation.

How plugins should design filesystem boundaries

The safest path is often the one the user never gets to specify directly.

Prefer identifiers over arbitrary filesystem paths

Instead of sending:

/var/www/site/wp-content/uploads/2026/report.pdf

from the browser, an application may pass an attachment ID, backup ID or other internal identifier.

The server then resolves that identifier to a known path.

This greatly reduces the amount of filesystem structure controlled by the request.

Use predefined roots

If a plugin manages several areas, define them explicitly.

For example:

  • WordPress installation root;
  • uploads root;
  • plugin storage root;
  • backup root.

The requested location can then be validated against the root associated with the current operation.

Do not authorize from a user-supplied root

This design defeats the purpose:

?root=/some/path
&file=another/path

if the request itself decides which directory should be considered trusted.

The security boundary should come from server-controlled configuration.

Reject failures securely

If path resolution fails, the default behavior should be rejection rather than falling back to a less strict path.

For example:

realpath() returns false
→ reject operation

is usually safer than:

realpath() returns false
→ use original untrusted string instead

Path traversal and WordPress uploads

Upload security and path traversal overlap when the user influences where a file is stored.

A secure upload handler needs to control:

  • which file is accepted;
  • what it is named;
  • where it is stored.

A file can pass MIME and extension validation and still be written to an inappropriate location if destination-path handling is vulnerable.

Conversely, a perfectly contained uploads directory does not make a dangerous file type safe.

For the file-type side of the problem, read Why Double-Extension Uploads Are Dangerous.

SVG shows why content validation is another layer

An SVG can have a correct path and a valid SVG MIME type but still require content sanitization.

That is why filesystem containment, file-type validation and content sanitization should remain separate security controls.

See Why WordPress Blocks SVG Uploads by Default for that distinction.

How TheOneWP prevents path traversal in File Manager

TheOneWP’s File Manager performs filesystem operations within defined roots rather than trusting incoming path strings as authorization.

Its verified implementation uses real-path resolution when checking existing filesystem locations, allowing it to compare the actual resolved destination with the root that the current volume is permitted to access.

Resolved paths must remain inside the approved root

The security model is based on containment.

A path that resolves within the configured root can proceed when the other authorization requirements are satisfied.

A path that resolves outside the permitted root is rejected.

This protects against traversal attempts that try to escape the file manager’s current filesystem scope.

The check also matters for symbolic links

Because the implementation works with resolved paths rather than only the displayed string, a path cannot simply appear to live inside the root while resolving somewhere unrelated through normal symlink traversal.

Dangerous filename checks are separate

File Manager also treats risky executable filename patterns as a separate security concern.

That means the module distinguishes between:

  • where a file operation is allowed to occur;
  • what kind of filename or upload is acceptable.

This is the correct separation of responsibilities.

Path containment does not replace upload validation, and upload validation does not replace path containment.

Back up before direct filesystem changes

Even authorized file operations can cause operational damage when someone edits or deletes the wrong resource.

TheOneWP’s Backup Manager provides a recovery layer before major filesystem maintenance.

Common path traversal prevention mistakes

Only searching for ../

This treats one textual representation as though it were the complete filesystem security model.

Resolve and authorize the final destination instead.

Using sanitize_text_field() on a path

Generic text sanitization is not directory containment.

The value may become cleaner while still identifying a forbidden filesystem location.

Using sanitize_file_name() on an entire path

sanitize_file_name() is intended for filenames, not for proving that an arbitrary path lies within a trusted directory.

Checking the prefix before canonicalization

A raw path can include traversal components or symlinks that change its actual destination.

Using an imprecise string prefix

A root named:

/uploads

must not accidentally authorize:

/uploads-backup

because the first characters happen to match.

Using realpath() without checking its result

realpath() can return false.

Code must treat that as an explicit state rather than accidentally converting the failure into an unsafe fallback.

Using realpath() directly for files that do not exist

Creation operations need parent-directory validation because the final file cannot be resolved before it exists.

Trusting a nonce as authorization

A nonce helps prove request intent within WordPress. It does not define filesystem scope.

Checking user capability but not the requested path

A privileged user can still submit an unexpected path.

Hardcoding wp-content paths

WordPress allows customized directory layouts, so plugins should use the platform’s path APIs rather than assuming one server structure.

Validating a path and later modifying it

If the application validates one value but opens, renames or deletes a transformed version, the validation may no longer describe the path actually used.

Path traversal prevention checklist for WordPress plugins

  • Treat every request-controlled path as untrusted.
  • Prefer internal IDs over raw filesystem paths in browser requests.
  • Define trusted filesystem roots server-side.
  • Use WordPress directory APIs instead of hardcoding standard paths.
  • Normalize existing paths before authorizing them.
  • Use realpath() appropriately for existing filesystem targets.
  • Check explicitly for realpath() failure.
  • Canonicalize the trusted root as well as the requested target.
  • Verify that the final resolved path remains inside the approved root.
  • Make containment comparisons directory-boundary aware.
  • Account for symbolic links.
  • Validate the existing parent directory when creating a new file.
  • Validate new filenames separately from their parent path.
  • Use sanitize_file_name() for filename cleaning, not as a replacement for path authorization.
  • Use validate_file() where appropriate to reject suspicious path structures.
  • Check WordPress user capabilities before filesystem operations.
  • Use nonces for request integrity where appropriate.
  • Remember that capabilities and nonces do not replace path validation.
  • Validate every destination during archive extraction.
  • Do not trust paths merely because the admin interface generated them.
  • Use the WordPress Filesystem API where it fits the operation.
  • Keep filesystem access as narrow as the feature requires.
  • Reject ambiguous or failed path resolution securely.
  • Keep backups before destructive file operations.

Related guides

Final thoughts

Path traversal vulnerabilities happen when an application confuses a user-controlled path with an authorized filesystem location.

The dangerous assumption is:

the path looks like it belongs here,
therefore it belongs here

That assumption breaks as soon as path components, symbolic links or other filesystem behavior change the final destination.

A stronger security model is:

receive requested resource
↓
authorize the user
↓
determine the trusted root
↓
resolve the real filesystem location
↓
verify containment
↓
perform the operation

For creation operations, where the final file does not yet exist, validate the canonical parent directory and validate the new filename separately.

Do not rely solely on filtering ../.

Do not rely solely on filename sanitization.

Do not rely solely on a valid WordPress nonce.

Do not assume an Administrator should automatically have unrestricted server filesystem access.

Each of those controls answers a different question.

The security boundary is ultimately the resolved filesystem destination.

If a plugin promises to manage files inside one directory, every read, write, rename, delete, upload and extraction operation should remain inside that directory after the filesystem has resolved the path.

That simple principle turns path validation from a collection of fragile string tricks into an actual filesystem boundary.

Simplify your WordPress stack

A modular WordPress toolkit. 104 focused tools.

Ultimately, you can build cleaner workflows, maintain fewer plugins and enable only the features each website actually needs.