PHP open_basedir is a boundary with footguns, not a sandbox
Using PHP open_basedir as defense in depth without overstating filesystem isolation
HP's open_basedir directive restricts the filesystem paths that PHP file functions may access. It is often presented as a one-line hardening change for shared hosting and multi-tenant applications. The directive is useful, but its scope and failure modes need to be understood before it becomes part of a security claim.
What the directive controls
The PHP manual describes open_basedir as a path restriction for file operations. It is an INI_ALL directive, which means it can be set in different configuration contexts. The manual notes that a script can tighten the setting at runtime, for example from /www/ to /www/tmp/.
The phrase "file operations" is important. open_basedir is not a kernel namespace, a container, or a complete command-execution boundary. It does not automatically stop a PHP extension from using a different code path, nor does it replace operating-system permissions, database privileges, or a web application firewall.
The directive works with an allowlist of path prefixes. A prefix match is not the same as a directory tree security model. A configuration that adds a broad parent path can accidentally authorize sibling directories the application never needed. Paths containing .., symbolic links, and stream wrappers are common places for configuration mistakes.
Set it at the process boundary
For PHP-FPM, put the value in the pool configuration with an administrative setting so that an application cannot relax it:
php_admin_value[open_basedir] = /srv/app/current:/srv/app/shared
Use the real paths that the application needs for uploads, temporary files, certificates, and logs. Do not add the document root just because a test produced a warning. Log the warning, identify the function that triggered it, and decide whether the path is required.
Shared hosting and multi-tenant deployments often need separate pools or containers per tenant. A single broad open_basedir for all tenants does not create tenant isolation. If two applications share a path, code in one tenant may still read files placed there by another, depending on the surrounding controls.
The directive's configuration scope also matters during deployment. A .user.ini setting may behave differently from a value enforced by the web server or PHP-FPM. Verify the effective configuration with phpinfo() in a non-production environment, then remove or protect the diagnostic page.
Test real application paths
Build a test matrix with the functions the application uses: file reads, uploads, temporary files, archive extraction, certificate loading, and log rotation. Run the tests as the service user and with the same working directory as production. Confirm that permitted paths work and that an unrelated path produces a denied access warning.
Review every intentional exception. A certificate path outside the application tree may be necessary, but the private key should still have restrictive ownership. A temporary directory that is world writable may be a better target for an attacker than the application directory. open_basedir does not fix those permissions.
Watch for applications that silently fall back after a file error. A failure that appears as a generic "configuration not found" message can hide a path restriction and lead an operator to broaden the setting without understanding the cause. Add an explicit startup check for required directories and report the effective value.
Combine it with controls that cover the rest
Use open_basedir as a defense-in-depth layer around file access. Pair it with OS-level users and mount permissions, a read-only application image where possible, isolated writable volumes, and explicit upload handling. For code that executes external commands, use a container or sandbox designed for that purpose rather than assuming PHP's file path restriction reaches the command.
Review changes to the directive like any other security boundary. Record the allowed paths, the owner of each path, and the reason it is needed. Remove stale entries during application retirement. A narrow allowlist is useful precisely because it makes the application's file dependencies visible.
References
- PHP manual, Description of core php.ini directives
- PHP manual, Where a configuration setting may be set
- PHP manual, The configuration file
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.