Skip to main content

Secure path sandboxing for untrusted environments

Project description

BoxedPath and SandboxPath: Secure File Path Handling

The Need

In modern applications, especially those handling untrusted code or user-uploaded files, secure access to the file system is critical. Unrestricted access can lead to vulnerabilities such as:

  • Accessing sensitive files outside the intended directory.
  • Exploiting path traversal to manipulate or steal data.

To mitigate these risks, applications need a mechanism to enforce strict constraints on file paths, ensuring they remain within a designated secure directory or "sandbox."


The Solution

The BoxedPath and Sandbox classes provide a robust solution for secure file path management:

  • BoxedPath:

    • Ensures any file path manipulation stays within a predefined sandbox directory.
    • Validates paths during initialization and every operation to prevent unauthorized access.
    • Supports common file operations like opening files, checking existence, and retrieving metadata.
  • Sandbox:

    • A specialized form of BoxedPath that represents the root of a sandbox.
    • Simplifies creating a constrained environment by setting the sandbox and path to the same directory.

Key Features

  • Path Validation: Ensures the real path of any file operation remains within the sandbox.
  • Path Composition: Allows safe concatenation of path segments while maintaining constraints.
  • Common Operations: Supports file operations like opening, checking existence, and accessing metadata within the sandbox.
  • Security Overrides: Provides explicit methods to access real paths when necessary but warns of security implications.

Sample Code

Here’s an example demonstrating the use of BoxedPath and Sandbox:

from boxedpath import BoxedPath, Sandbox

# Define a secure sandbox directory
sandbox = Sandbox('/secure/sandbox')

# Create a path within the sandbox
file_path = sandbox / 'example.txt'

# Check if the file exists
if file_path.exists():
    print(f"File found: {file_path}")
    # Open the file for reading
    with file_path.open('r') as f:
        content = f.read()
        print("File Content:", content)
else:
    print(f"File does not exist: {file_path}")

# Attempting to create a path outside the sandbox will raise a PermissionError
try:
    invalid_path = sandbox / '../outside.txt'
except PermissionError as e:
    print("Security Error:", e)

Benefits

  • Enhanced Security: Restricts file operations to a designated sandbox, preventing unauthorized access.
  • Ease of Use: Provides intuitive APIs for path manipulation and file operations.
  • Flexibility: Handles complex path manipulations while maintaining security constraints.

These classes are essential tools for any application needing controlled access to the file system, ensuring security without compromising functionality.

Migration from Path to BoxedPath

Migrating from Python's built-in pathlib.Path to BoxedPath involves understanding and adapting to the security constraints that BoxedPath enforces. While pathlib.Path offers a versatile API for file system path manipulation, it does not inherently enforce security boundaries. BoxedPath, on the other hand, ensures all operations remain within a defined sandbox for enhanced security.


Steps for Migration

  1. Initialization: Replace pathlib.Path instances with BoxedPath. You must specify the sandbox directory as part of the BoxedPath initialization.

    Before:

    from pathlib import Path
    
    path = Path('/secure/sandbox/example.txt')
    

    After:

    from boxedpath import BoxedPath
    
    path = BoxedPath('/secure/sandbox/example.txt', '/secure/sandbox')
    

  1. Path Joining: Use / for joining paths, which is consistent with pathlib.Path. Ensure that all resulting paths stay within the sandbox.

    Before:

    path = Path('/secure/sandbox') / 'example.txt'
    

    After:

    sandbox = Sandbox('/secure/sandbox')  # shorthand for BoxedPath('/secure/sandbox', '/secure/sandbox')
    path = sandbox / 'example.txt'
    

  1. Validation: Paths outside the sandbox are automatically blocked. This is a significant change from pathlib.Path, which allows unrestricted path traversal.

    Before:

    path = Path('/secure/sandbox') / '../outside.txt'  # No validation
    print(path.resolve())  # This resolves to a path outside the sandbox
    

    After:

    try:
        path = sandbox / '../outside.txt'  # Raises PermissionError
    except PermissionError as e:
        print(f"Security Error: {e}")
    

  1. Common File Operations: Operations like opening a file, checking if it exists, or retrieving metadata work similarly but are constrained by the sandbox.

    Before:

    with path.open('r') as file:
        content = file.read()
    
    exists = path.exists()
    stats = path.stat()
    

    After:

    with path.open('r') as file:
        content = file.read()
    
    exists = path.exists()
    stats = path.stat()
    

  1. Real Path Access: If your application requires access to the real (absolute) path, use BoxedPath.insecure_unrestrained_realpath() cautiously. This bypasses sandbox constraints and should only be used in trusted scenarios.

    Before:

    real_path = path.resolve()
    

    After:

    real_path = path.insecure_unrestrained_realpath()
    

Benefits of Migration

  • Security First: Automatically prevents directory traversal and access to unintended parts of the file system.
  • Minimal Changes: Retains the familiar API of pathlib.Path, easing the transition.
  • Controlled Operations: Enforces path validation at every operation, reducing potential vulnerabilities.

Migration Example

Original Code Using pathlib.Path:

from pathlib import Path

base_path = Path('/secure/sandbox')
file_path = base_path / 'example.txt'

if file_path.exists():
    with file_path.open('r') as f:
        print(f.read())

Updated Code Using BoxedPath:

from boxedpath import BoxedPath

sandbox = BoxedPath('/secure/sandbox', '/secure/sandbox')
file_path = sandbox / 'example.txt'

if file_path.exists():
    with file_path.open('r') as f:
        print(f.read())

This structured migration ensures the application benefits from the enhanced security provided by BoxedPath without significant code rewrites.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

owasp_untrust_boxedpath-0.1.0.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

owasp_untrust_boxedpath-0.1.0-py3-none-any.whl (10.4 kB view details)

Uploaded Python 3

File details

Details for the file owasp_untrust_boxedpath-0.1.0.tar.gz.

File metadata

  • Download URL: owasp_untrust_boxedpath-0.1.0.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.11

File hashes

Hashes for owasp_untrust_boxedpath-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e6b4d16db435dfa670119889c75a85ce0f19a0b5354ab667dc706a5dca8d0dff
MD5 8a6ea3215337516e13573e559898dfa9
BLAKE2b-256 c4e71a4eaad962e14ba431b8d95828d2f972cf3d3edfee0af64cbf6f6e8ab9ae

See more details on using hashes here.

File details

Details for the file owasp_untrust_boxedpath-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for owasp_untrust_boxedpath-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8ebb46d89e9fa5ff1e4308ac4e81efad44aab442b4c30f0ca00b5017bcd59aae
MD5 98b36cdc1882f252bb9abf73c11e4762
BLAKE2b-256 f0c71e0350bb0276e20c4d3a9d7f8e7317decefcd159d87912c8351bc7c2ae0a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page