Skip to content

Commit 4138016

Browse files
authored
docs: document last-error state persistence in worker mode (#2738)
A worker script runs as one long PHP request, so error_get_last(), libxml_get_errors() and libxml_get_last_error() keep returning what an earlier request left behind until it is cleared. These messages can embed fragments of another client's input or server paths. Add this state to the list of what persists across worker requests, and recommend clearing it with error_clear_last() and libxml_clear_errors().
1 parent f2df5c3 commit 4138016

2 files changed

Lines changed: 2 additions & 1 deletion

File tree

‎docs/security.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ These are the surfaces FrankenPHP owns. A vulnerability here is a FrankenPHP vul
4646

4747
- **Request to superglobal mapping** (`cgi.go`, `frankenphp_register_server_vars`): building `$_SERVER`, `REMOTE_ADDR`, `SCRIPT_NAME`, `PATH_INFO`, and the other CGI variables from the request.
4848
- **PHP script-path resolution**: the request path is split on `split_path` (`.php` by default) into `SCRIPT_NAME` / `PATH_INFO`, then joined to the document root with `sanitizedPathJoin` (`filepath.Join(root, filepath.Clean("/"+reqPath))`), which keeps `SCRIPT_FILENAME` from escaping the document root (path traversal). The `php_server` directive additionally sets a default `try_files` rewrite that routes requests to existing files or the front controller, mitigating the classic PHP-FPM pitfall of executing the wrong file.
49-
- **Worker-mode state isolation**: FrankenPHP resets `$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`, and `$_REQUEST` between requests, and explicitly clears `$_SESSION` (which would otherwise leak between requests), but **`$_ENV` is not reset**, and `putenv()` writes, `static` variables, class static properties, globals, and runtime engine settings (`ini_set()`, the default stream context, the default timezone, the working directory, ...) persist across requests on the same thread. Request- or user-specific data left in that state can leak into a later request (see [Worker Mode](worker.md#state-persistence)).
49+
- **Worker-mode state isolation**: FrankenPHP resets `$_GET`, `$_POST`, `$_COOKIE`, `$_FILES`, `$_SERVER`, and `$_REQUEST` between requests, and explicitly clears `$_SESSION` (which would otherwise leak between requests), but **`$_ENV` is not reset**, and `putenv()` writes, `static` variables, class static properties, globals, and runtime engine settings (`ini_set()`, the default stream context, the default timezone, the working directory, ...), and the last-error state of PHP and its extensions (`error_get_last()`, `libxml_get_errors()`, ...) persist across requests on the same thread. Request- or user-specific data left in that state can leak into a later request (see [Worker Mode](worker.md#state-persistence)).
5050
- **Per-thread environment sandboxing**: `frankenphp_putenv()` / `frankenphp_getenv()` operate on a thread-local `sandboxed_env`. `putenv()` writes stay inside that sandbox and never reach the process-global OS environment, so a secret set by one request cannot leak to child processes (`proc_open`, `exec`, ...), to the Go/Caddy side, or to other requests. A child process that must see such a variable has to be given it explicitly, through the environment argument of `proc_open()` or `exec()` (see [Internals](internals.md#per-thread-environment-sandboxing)).
5151
- **CGO memory boundary**: Go string pinning and `C.CString()` / `free()` lifetimes across the Go ↔ C boundary.
5252
- **Caddy admin API**: the `/frankenphp/workers/restart` and `/frankenphp/threads` endpoints, exposed through Caddy's admin API (which listens on `localhost:2019` by default). Exposing that endpoint beyond localhost is an operator decision.

‎docs/worker.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ Because worker mode keeps the PHP process alive between requests, the following
243243
- **Global variables**: Variables in the global scope of the worker script persist between requests.
244244
- **In-memory caches**: Any data stored in memory (arrays, objects) outside the request handler persists.
245245
- **Runtime engine settings**: Changes made with `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` or `set_exception_handler()` stay in effect for later requests. Never store request- or user-specific values there, such as credentials in the default stream context: pass an explicit context to each call instead.
246+
- **Last-error state of PHP and its extensions**: `error_get_last()`, `libxml_get_errors()` and `libxml_get_last_error()` return what an earlier request left behind until it is cleared. These messages can contain fragments of another client's input or server paths: call `error_clear_last()` and `libxml_clear_errors()` at the end of each request, or before reading them.
246247

247248
This is by design and is what makes worker mode fast. However, it requires attention to avoid unintended side effects:
248249

0 commit comments

Comments
 (0)