securejoin

package module
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2023 License: BSD-3-Clause Imports: 6 Imported by: 524

README

filepath-securejoin

Build Status

An implementation of SecureJoin, a candidate for inclusion in the Go standard library. The purpose of this function is to be a "secure" alternative to filepath.Join, and in particular it provides certain guarantees that are not provided by filepath.Join.

NOTE: This code is only safe if you are not at risk of other processes modifying path components after you've used SecureJoin. If it is possible for a malicious process to modify path components of the resolved path, then you will be vulnerable to some fairly trivial TOCTOU race conditions. There are some Linux kernel patches I'm working on which might allow for a better solution.

In addition, with a slightly modified API it might be possible to use O_PATH and verify that the opened path is actually the resolved one -- but I have not done that yet. I might add it in the future as a helper function to help users verify the path (we can't just return /proc/self/fd/<foo> because that doesn't always work transparently for all users).

This is the function prototype:

func SecureJoin(root, unsafePath string) (string, error)

This library guarantees the following:

  • If no error is set, the resulting string must be a child path of root and will not contain any symlink path components (they will all be expanded).

  • When expanding symlinks, all symlink path components must be resolved relative to the provided root. In particular, this can be considered a userspace implementation of how chroot(2) operates on file paths. Note that these symlinks will not be expanded lexically (filepath.Clean is not called on the input before processing).

  • Non-existent path components are unaffected by SecureJoin (similar to filepath.EvalSymlinks's semantics).

  • The returned path will always be filepath.Cleaned and thus not contain any .. components.

A (trivial) implementation of this function on GNU/Linux systems could be done with the following (note that this requires root privileges and is far more opaque than the implementation in this library, and also requires that readlink is inside the root path):

package securejoin

import (
	"os/exec"
	"path/filepath"
)

func SecureJoin(root, unsafePath string) (string, error) {
	unsafePath = string(filepath.Separator) + unsafePath
	cmd := exec.Command("chroot", root,
		"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}
	expanded := string(output)
	return filepath.Join(root, expanded), nil
}

License

The license of this project is the same as Go, which is a BSD 3-clause license available in the LICENSE file.

Documentation

Overview

Package securejoin is an implementation of the hopefully-soon-to-be-included SecureJoin helper that is meant to be part of the "path/filepath" package. The purpose of this project is to provide a PoC implementation to make the SecureJoin proposal (https://github.com/golang/go/issues/20126) more tangible.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsNotExist added in v0.2.1

func IsNotExist(err error) bool

IsNotExist tells you if err is an error that implies that either the path accessed does not exist (or path components don't exist). This is effectively a more broad version of os.IsNotExist.

func SecureJoin

func SecureJoin(root, unsafePath string) (string, error)

SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library of functions as the VFS. If in doubt, use this function over SecureJoinVFS.

func SecureJoinVFS added in v0.2.0

func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error)

SecureJoinVFS joins the two given path components (similar to Join) except that the returned path is guaranteed to be scoped inside the provided root path (when evaluated). Any symbolic links in the path are evaluated with the given root treated as the root of the filesystem, similar to a chroot. The filesystem state is evaluated through the given VFS interface (if nil, the standard os.* family of functions are used).

Note that the guarantees provided by this function only apply if the path components in the returned string are not modified (in other words are not replaced with symlinks on the filesystem) after this function has returned. Such a symlink race is necessarily out-of-scope of SecureJoin.

Volume names in unsafePath are always discarded, regardless if they are provided via direct input or when evaluating symlinks. Therefore:

"C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"

Types

type VFS added in v0.2.0

type VFS interface {
	// Lstat returns a FileInfo describing the named file. If the file is a
	// symbolic link, the returned FileInfo describes the symbolic link. Lstat
	// makes no attempt to follow the link. These semantics are identical to
	// os.Lstat.
	Lstat(name string) (os.FileInfo, error)

	// Readlink returns the destination of the named symbolic link. These
	// semantics are identical to os.Readlink.
	Readlink(name string) (string, error)
}

VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is equivalent to using the standard os.* family of functions. This is mainly used for the purposes of mock testing, but also can be used to otherwise use SecureJoin with VFS-like system.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL