Sitemap
System Weakness

System Weakness is a publication that specialises in publishing upcoming writers in cybersecurity and ethical hacking space. Our security experts write to make the cyber universe more secure, one vulnerability at a time.

Pathlib in Python: Modern, Secure File Path Handling

3 min readSep 15, 2025

--

  • When building Python applications, working with files and directories is unavoidable.
  • Traditionally, developers relied on plain strings to represent file paths, concatenating them with operators like + or using functions like os.path.join.

But here’s the problem: strings don’t understand paths. To a string, ../secrets.txt is just a sequence of characters — not a potential security risk.

🔑Enter pathlib: Python’s modern, object-oriented library for working with filesystem paths.🔑

🔹 Why Pathlib?

  • Object-oriented paths: A path is no longer a fragile string but a Path object with methods to manipulate and validate safely.
  • Cross-platform safety: Windows uses backslashes (\), Linux/macOS use forward slashes (/). pathlib automatically handles this.
  • Readable code: Cleaner and more intuitive APIs compared to string concatenation.
  • Security benefits: Provides tools to resolve, normalize, and restrict file access — reducing vulnerabilities like Path Traversal.

🔹 The Problem with Strings

Let’s look at a vulnerable example using plain strings:

import os

BASE_DIR = "/srv/reports"

def get_report(filename):
full_path = os.path.join(BASE_DIR, filename) # unsafe join
with open(full_path, "r") as f:
return f.read()

# Attacker input:
print(get_report("../../etc/passwd"))
# 🔥 Reads sensitive file outside BASE_DIR

If an attacker passes ../../etc/passwd as filename, os.path.join happily constructs /srv/reports/../../etc/passwd.

After resolution, this points to /etc/passwd — a sensitive system file.

🚨 This is a classic Path Traversal vulnerability.🚨

🔹 Pathlib to the Rescue

  • Now let’s see the same code with pathlib:
from pathlib import Path

BASE_DIR = Path("/srv/reports").resolve()

def get_report(filename):
requested_path = (BASE_DIR / filename).resolve()

# Security check: ensure path is still inside BASE_DIR
if not str(requested_path).startswith(str(BASE_DIR)):
raise ValueError("Invalid file request")

return requested_path.read_text()

# Attacker input blocked:
print(get_report("../../etc/passwd"))
# ❌ Raises ValueError instead of reading system file

✅ Why is this safer?

➡️BASE_DIR = Path(“/srv/reports”).resolve()

  • Converts the directory into a Path object.
  • .resolve() normalizes the path, removing . and …

➡️(BASE_DIR / filename).resolve()

  • Uses / operator (overloaded for paths) to join safely.
  • .resolve() collapses malicious inputs like ../../etc/passwd into their true location.

➡️Security check ensures the resolved file path stays within BASE_DIR.

Get Siddiquimohammad’s stories in your inbox

Join Medium for free to get updates from this writer.

🔹 Key Security Features of Pathlib

  • .resolve() → Expands symlinks, removes traversal tricks.
  • .is_absolute() → Detects if a path is trying to escape with an absolute location.
  • .parts → Lets you inspect each component of the path (helpful for spotting ..).
  • Readable methods → e.g., .suffix for file extension, .name for filename only.

Example:

from pathlib import Path

# Create a Path object pointing to a suspicious file
p = Path("../etc/passwd")

# Break down the path into parts
print("Parts:", p.parts)
# Output: ('..', 'etc', 'passwd')

# Check if it's an absolute path
print("Is absolute?:", p.is_absolute())
# Output: False

# Get the file name only
print("Name:", p.name)
# Output: 'passwd'

# Get the file extension (suffix)
print("Suffix:", p.suffix)
# Output: '' (empty, since 'passwd' has no extension)

# Normalize the path (resolve removes '..')
print("Resolved:", p.resolve())
# Example Output: '/home/user/etc/passwd'

🔹 Secure Coding Best Practices with Pathlib

  1. Always resolve paths before use
safe_path = Path(user_input).resolve()
  1. Enforce a root directory lock-in

    if not str(safe_path).startswith(str(BASE_DIR)):
    raise ValueError(“Unauthorized path”)
  2. Whitelist file extensions
if safe_path.suffix.lower() not in {".txt", ".log"}:
raise ValueError("Disallowed file type")

3. Never trust raw user input in file operations

🔹 Why This Matters for Secure Python Coding

  • Many security vulnerabilities arise from treating user input as trusted strings.
  • With paths, attackers abuse this by injecting traversal sequences (../, %2e%2e/) to escape directories and access sensitive files.

By using pathlib, you:

  • Force your application to think in paths, not strings.
  • Gain built-in normalization, making it harder for attackers to sneak past.
  • Make your code cleaner, more Pythonic, and secure.

🔹 Final Thoughts

  • In modern Python, pathlib should be your default choice for file path handling.
  • It’s not just about developer convenience — it’s about writing secure, maintainable, and cross-platform code.

If you’re building web apps, microservices, or scripts that process files:

👉 Replace os.path + string concatenation with pathlib.Path.

👉 Always resolve and validate user input.

This small shift can prevent one of the oldest yet still critical vulnerabilities: Path Traversal.

--

Siddiquimohammad
Siddiquimohammad

Written by Siddiquimohammad

Cybersecurity strategist | Offense meets defense | Cloud & AI security, DevSecOps, Red Team tactics & Blue Team resilience in one security brain

System Weakness
System Weakness

Published in System Weakness

System Weakness is a publication that specialises in publishing upcoming writers in cybersecurity and ethical hacking space. Our security experts write to make the cyber universe more secure, one vulnerability at a time.