Owasp.Untrust.BoxedPath
1.2.0
dotnet add package Owasp.Untrust.BoxedPath --version 1.2.0
NuGet\Install-Package Owasp.Untrust.BoxedPath -Version 1.2.0
<PackageReference Include="Owasp.Untrust.BoxedPath" Version="1.2.0" />
<PackageVersion Include="Owasp.Untrust.BoxedPath" Version="1.2.0" />
<PackageReference Include="Owasp.Untrust.BoxedPath" />
paket add Owasp.Untrust.BoxedPath --version 1.2.0
#r "nuget: Owasp.Untrust.BoxedPath, 1.2.0"
#:package Owasp.Untrust.BoxedPath@1.2.0
#addin nuget:?package=Owasp.Untrust.BoxedPath&version=1.2.0
#tool nuget:?package=Owasp.Untrust.BoxedPath&version=1.2.0
Owasp.Untrust.BoxedPath (.NET)
A secure path management library for .NET designed to prevent Directory Traversal (Path Traversal) and Symbolic Link (Symlink) Jailbreak attacks. It provides a robust "sandbox" mechanism to ensure that all file system operations remain strictly within a defined root directory.
Changes
- 1.2.0 - Renamed namespace to plural Owasp.Untrust.BoxedPaths to avoid "class name same as namespace name" problem
- 1.1.0 - [DEPRECTATED/REVERTED]
Renamed primary class from BoxedPath to Path to avoid "class name same as namespace name" problem- !!! this change conflicts with System.IO.Path - 1.0.0 - First release
The Need
Modern .NET applications frequently interact with the file system. However, accepting file paths from untrusted sources (user input, uploads, configuration files) introduces critical security risks:
- Directory Traversal: Attackers using
../sequences to access sensitive system files (e.g.,../../windows/system32/config/SAM). - Symlink Jailbreak: Attackers using symbolic links (symlinks) to trick the application into reading or writing files outside the intended directory, even if the path looks safe lexically.
- TOCTOU Vulnerabilities: Time-of-Check to Time-of-Use race conditions where a path is validated but then swapped (e.g., via symlink) before it is used.
Standard .NET classes like System.IO.Path provide lexical manipulation but do not enforce security boundaries or handle advanced symlink resolution securely.
The Solution
Owasp.Untrust.BoxedPath introduces two core concepts:
PathSandbox: Defines the secure root directory and the security policy (e.g., whether symlinks are allowed).BoxedPath: An immutable, secure wrapper around a file path. It guarantees that the path it holds has been validated to be inside its associated sandbox.
All operations on BoxedPath (like Combine, GetParent) return a new, validated BoxedPath or throw a SecurityException if the operation would result in a path outside the sandbox.
Key Features
- Strict Sandboxing: Enforces that all paths must resolve to a location inside the sandbox root.
- Symlink Defense: Offers a robust, component-by-component path traversal algorithm (
ResolveAndValidatePath) that physically resolves symlinks to prevent jailbreaks. - TOCTOU Protection: Validation resolves the physical path, ensuring that the path used for I/O is the same one that was checked.
- Drop-in API Feel: Designed to mimic the
System.IO.PathandFileInfoAPIs for ease of adoption. - Secure Wrappers: Provides
BoxedFileInfo,BoxedDirectoryInfo, andBoxedFileStreamto perform I/O operations safely without exposing the raw path.
Installation
Install via NuGet:
dotnet add package Owasp.Untrust.BoxedPath
Usage
1. Initialization
Create a PathSandbox instance to define your secure root.
using Owasp.Untrust.BoxedPaths; // note plural Path_s_
// Define a sandbox rooted at "C:\Safe\Uploads"
// Default policy: DISALLOW (Symlinks are followed but must stay inside the sandbox)
PathSandbox sandbox = PathSandbox.BoxRoot(@"C:\Safe\Uploads");
2. Creating Secure Paths
Use the Of() factory method to create a BoxedPath from an untrusted string.
try
{
// User input: "user_data.txt" (Safe)
BoxedPath safePath = BoxedPath.Of(sandbox, "user_data.txt");
// User input: "../../../windows/system.ini" (Malicious)
// THROWS SecurityException immediately!
BoxedPath maliciousPath = BoxedPath.Of(sandbox, "../../../windows/system.ini");
}
catch (SecurityException ex)
{
Console.WriteLine($"Attack blocked: {ex.Message}");
}
3. Path Manipulation
Combine paths securely. The library ensures the result is still valid.
BoxedPath basePath = BoxedPath.Of(sandbox, "users");
// Safe combination: C:\Safe\Uploads\users\alice
BoxedPath userPath = BoxedPath.Combine(basePath, "alice");
// Malicious combination attempt
// THROWS SecurityException
BoxedPath hackAttempt = BoxedPath.Combine(basePath, "../../admin");
4. Performing File I/O
Crucial: Do not convert the BoxedPath to a string to use standard File methods directly, as that breaks the chain of trust. Instead, use the secure wrappers or the ValidateAndExpose() method at the very last moment.
Option A: Use Secure Wrappers (Recommended)
// Check existence safely
if (BoxedFile.Exists(safePath))
{
// Read text securely
string content = BoxedFile.ReadAllText(safePath);
}
// Use FileInfo wrapper (Does NOT expose .FullName)
var fileInfo = new BoxedFileInfo(safePath);
long size = fileInfo.Length;
// Secure FileStream
using (var stream = new BoxedFileStream(safePath, FileMode.Open))
{
// ... read/write ...
}
Option B: Explicit Exposure (Use with Caution)
If you must use an API that strictly requires a string path:
// ValidateAndExpose either relies on previous validation (for an absolute path) or validates the path.
// The raw physical path string is returned.
// Use this result IMMEDIATELY and do not store it.
string rawPath = safePath.ValidateAndExpose();
System.IO.File.Delete(rawPath); // Note that this is only for example purposes! Better use BoxedFile.Delete(safePath);
Advanced Configuration
Symlink Policy
You can control how symbolic links are handled.
// Default: Follows symlinks, but throws if the target is outside the sandbox.
var secureSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.DISALLOW);
// Dangerous: Allows symlinks to point anywhere (relies on OS permissions).
var looseSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.UNCHECKED_SYMLINKS);
Max Link Depth
To prevent infinite loops or Denial of Service (DoS) via "symlink bombs," you can limit the recursion depth.
// Limit to 20 link hops (Default is 5)
var deepSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.DISALLOW, maxLinkFollows: 20);
Contributing
This project is an official OWASP contribution. Issues and Pull Requests are welcome on the GitHub Repository.
License
Licensed under the Apache License 2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.