Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 21, 20264 min read

Preventing Path Traversal: Secure File System Access for Developers

Master path traversal prevention in Node.js and PHP. Learn secure file handling techniques to stop attackers from accessing sensitive server directories.

securitynodejsphpweb developmentpath traversalbackendWeb

During an on-call rotation last year, I spent about three hours debugging a weird file-read error that turned out to be a classic path traversal attempt. A user was injecting ../../etc/passwd into a download parameter, and our application was blindly passing that string directly to the filesystem. It’s a sobering reminder that even simple file-fetching logic can become a gateway to total system compromise if you don't treat user input as hostile.

Understanding the Path Traversal Threat

At its core, path traversal—also known as directory traversal—is an exploit that allows an attacker to access files and directories stored outside the intended web root. By manipulating variables that reference files with "dot-dot-slash" (../) sequences, an attacker can navigate your server's file hierarchy.

If your code takes a filename from a URL parameter and constructs a file path like this:

JAVASCRIPT
// DON'T DO THIS
const filePath = path.join(CE9178">'/var/www/uploads', req.query.filename);
fs.readFile(filePath, (err, data) => { ... });

You are leaving the front door wide open. Even if you use path.join, it doesn't prevent the traversal if the user provides an absolute path or enough ../ segments to climb out of the intended directory.

Secure File Handling in Node.js

In Node.js, the best defense is to validate the resolved path rather than just the input string. You want to ensure that once the path is fully resolved, it still starts with your designated storage directory.

Here is how I prefer to handle it:

JAVASCRIPT
const path = require(CE9178">'path');
const fs = require(CE9178">'fs');

function getSecureFile(requestedPath) {
    const safeBase = path.resolve(CE9178">'/var/www/uploads');
    const userPath = path.resolve(safeBase, requestedPath);

    if (!userPath.startsWith(safeBase)) {
        throw new Error(CE9178">'Access Denied: Path traversal detected.');
    }

    return userPath;
}

By using path.resolve(), you convert relative sequences into an absolute path. Checking that the result starts with your safeBase is a robust way to ensure the file is where it belongs. While we are discussing file security, remember that secure file uploads from the ground up: A developer's guide is essential reading, as traversal is often paired with malicious file uploads.

Defensive Strategies in PHP

PHP developers often fall into the trap of using basename() to strip directory info, but that can sometimes be bypassed depending on your server configuration. A cleaner approach is to use realpath() and compare it against your allowed directory.

PHP
$baseDir = '/var/www/uploads/';
$requestedFile = $_GET['file'];

#6A9955">// Resolve the path
$realBase = realpath($baseDir);
$realRequested = realpath($baseDir . $requestedFile);

if ($realRequested === false || strpos($realRequested, $realBase) !== 0) {
    die("Access Denied");
}

#6A9955">// Proceed to read the file

This pattern prevents the attacker from escaping the uploads folder. Just like we discussed in preventing IDOR vulnerabilities in Laravel with attribute-based access control, you should always verify that the user has permission to access the specific resource before letting the filesystem interact with it.

Why Input Validation Isn't Enough

Many developers try to "sanitize" input by stripping out ../ strings. I’ve seen this fail repeatedly. Attackers can use URL encoding (like %2e%2e%2f) or double encoding to bypass simple string-replacement filters.

If you rely on blacklisting characters, you're playing a game of catch-up you cannot win. Instead, use a whitelist approach or, better yet, don't use the filename provided by the user at all.

The "No-Input" Rule

The safest way to handle file downloads is to map user IDs or database UUIDs to file paths in your backend.

  1. Store files with a generated hash as the filename (e.g., a1b2c3d4.pdf).
  2. Map that hash to the original filename in your database.
  3. When the user requests a file, look up the hash by ID.
  4. Serve the file from your code using the hash, never the user-provided string.

This eliminates path traversal entirely because the user never touches the filesystem path. If you are handling data objects, ensure you're also preventing mass assignment vulnerabilities with DTOs in Laravel and Express to keep your data layer clean and isolated.

Frequently Asked Questions

Q: Is path.normalize() enough to stop traversal in Node.js? A: No. normalize() only cleans up the string; it doesn't check if the resulting path stays within your intended directory. Always use path.resolve() and check the prefix.

Q: Does using a web server like Nginx or Apache protect me from path traversal? A: Not necessarily. While they can block some requests, they don't know your application's logic. If your application code accepts a path parameter and processes it, the vulnerability resides in your code, not the server configuration.

Q: What if I need to allow subdirectories? A: If you must allow subdirectories, validate that the requested path does not contain null bytes (in older PHP versions) and use a strict whitelist of allowed characters (alphanumeric only).

Wrapping Up

Managing filesystem access is a high-stakes task. I’ve found that the best approach is to treat the filesystem as an untrusted external service. Whenever I write code that touches a file, I assume the input is malicious.

Next time, I’m planning to explore how to integrate these checks into a middleware layer so we don't have to repeat this logic in every controller. It’s better to have a single, audited point of entry for file access than to sprinkle validation logic throughout the codebase. Keep your paths absolute, your validations strict, and your file names abstracted.

Back to Blog

Similar Posts

SecurityJune 21, 20264 min read

Preventing Prototype Pollution in Node.js: A Security Guide

Preventing prototype pollution is essential for Node.js security. Learn how to stop recursive object injection and harden your code against common attacks.

Read more
Close-up of a steel padlock on a mesh fence, symbolizing protection and security.
SecurityJune 21, 20264 min read

SSRF Prevention: Securing Cloud-Native Node.js Microservices

Master SSRF prevention for your Node.js microservices. Learn how to combine application-level validation with network-level isolation to secure your cloud.

Read more
Focus on password security with white keyboard tiles spelling 'PASSWORD' on a coral background.
SecurityJune 20, 20265 min read

Handling secrets securely to prevent accidental credential leakage

Handling secrets securely is non-negotiable for production apps. Learn how to stop leaking API keys and database credentials in your codebase today.

Read more