Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions docs/cn/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,27 @@ FrankenPHP 在一个包含四个不同参与者的栈中运行:

可信计算基有三项职责。FrankenPHP 中的安全缺陷存在于其中之一:

1. **忠实传输**:将请求映射到 PHP 超全局变量和 php://input 中,并将 PHP 的输出和请求头传回客户端,而不引入注入(请求头/CRLF 注入、请求走私、执行错误文件)。
2. **隔离**:防止请求范围的状态在不同请求之间、PHP 线程之间以及工作进程迭代之间交叉。
3. **内存安全**:管理 CGO 边界(Go ↔ C/PHP),不损坏内存。
1. **忠实传输**:将请求映射到 PHP 超全局变量和 php://input 中,并将 PHP 的输出和请求头传回客户端,而不引入注入(请求头/CRLF 注入、请求走私、执行错误文件)。
2. **隔离**:防止请求范围的状态在不同请求之间、PHP 线程之间以及工作进程迭代之间交叉。
3. **内存安全**:管理 CGO 边界(Go ↔ C/PHP),不损坏内存。

## 范围:FrankenPHP 自身的攻击面

以下是 FrankenPHP 拥有的攻击面。这里的漏洞是 FrankenPHP 的漏洞:

- **请求到超全局变量映射**(`cgi.go`、`frankenphp_register_server_vars`):从请求构建 `$_SERVER`、`REMOTE_ADDR`、`SCRIPT_NAME`、`PATH_INFO` 和其他 CGI 变量。
- **PHP 脚本路径解析**:请求路径在 `split_path`(默认为 `.php`)处被分割为 `SCRIPT_NAME` / `PATH_INFO`,然后通过 `sanitizedPathJoin`(`filepath.Join(root, filepath.Clean("/"+reqPath))`)与文档根目录连接,这可以防止 `SCRIPT_FILENAME` 逃逸文档根目录(路径遍历)。`php_server` 指令还会额外设置一个默认的 `try_files` 重写规则,将请求路由到现有文件或前端控制器,从而缓解了 PHP-FPM 经典陷阱——执行错误文件的问题。
- **工作模式状态隔离**:FrankenPHP 在请求之间重置 `$_GET`、`$_POST`、`$_COOKIE`、`$_FILES`、`$_SERVER` 和 `$_REQUEST`,显式清除 `$_SESSION`(否则会在请求之间泄露),以及 `error_get_last()` 和 `libxml_get_errors()` 返回的错误,`http_get_last_response_headers()` 返回的请求头,`strtok()` 状态,以及 `output_add_rewrite_var()` 添加的变量,但 **`$_ENV` 不会被重置**,并且 `putenv()` 写入、`static` 变量、类静态属性、全局变量和运行时引擎设置(`ini_set()`、默认流上下文、默认时区、工作目录等)会在同一线程的请求之间持久存在。留在该状态中的请求或用户特定数据可能会泄露到后续请求中(参见 [工作模式](worker.md#state-persistence))。
- **每线程环境沙盒**:`frankenphp_putenv()` / `frankenphp_getenv()` 操作的是线程本地的 `sandboxed_env`。`putenv()` 写入保留在该沙盒内,绝不会到达进程全局的操作系统环境,因此一个请求设置的秘密不会泄露给子进程(`proc_open`、`exec` 等)、Go/Caddy 端或其他请求。必须看到此类变量的子进程必须通过 `proc_open()` 或 `exec()` 的环境参数明确地提供给它(参见 [内部原理](internals.md#per-thread-environment-sandboxing))。
- **CGO 内存边界**:Go 字符串固定以及 `C.CString()` / `free()` 在 Go ↔ C 边界上的生命周期。
- **Caddy 管理 API**:`/frankenphp/workers/restart` 和 `/frankenphp/threads` 端点,通过 Caddy 的管理 API(默认监听 `localhost:2019`)暴露。将该端点暴露到 localhost 之外是操作员的决定。
- **受信任代理处理**:传入的 `X-Forwarded-*` 请求头总是作为不可信的 `$_SERVER['HTTP_X_FORWARDED_*']` 值到达 PHP;只有当配置了 [`trusted_proxies`](production.md#running-behind-a-reverse-proxy) 时,它们才被信任用于推导真实的客户端 IP 和协议。
- **慢速请求体**:客户端宣布请求体后,如果缓慢发送或停滞,会长时间占用处理线程。在一个有界线程池中,足够多的此类连接会耗尽线程池(慢速 POST DoS)。FrankenPHP 默认对请求体读取应用 60 秒的空闲超时([`request_body_timeout`](config.md#caddyfile-config)),并在每次读取前重置截止时间,这样任何大小的稳定上传都能成功,而停滞的上传会被切断并释放线程。
- **请求到超全局变量映射**(`cgi.go`、`frankenphp_register_server_vars`):从请求构建 `$_SERVER`、`REMOTE_ADDR`、`SCRIPT_NAME`、`PATH_INFO` 和其他 CGI 变量。
- **PHP 脚本路径解析**:请求路径在 `split_path`(默认为 `.php`)处被分割为 `SCRIPT_NAME` / `PATH_INFO`,然后通过 `sanitizedPathJoin`(`filepath.Join(root, filepath.Clean("/"+reqPath))`)与文档根目录连接,这可以防止 `SCRIPT_FILENAME` 逃逸文档根目录(路径遍历)。`php_server` 指令还会额外设置一个默认的 `try_files` 重写规则,将请求路由到现有文件或前端控制器,从而缓解了 PHP-FPM 经典陷阱——执行错误文件的问题。
- **工作模式状态隔离**:FrankenPHP 在请求之间重置 `$_GET`、`$_POST`、`$_COOKIE`、`$_FILES`、`$_SERVER` 和 `$_REQUEST`,显式清除 `$_SESSION`(否则会在请求之间泄露),以及 `error_get_last()` 和 `libxml_get_errors()` 返回的错误,`http_get_last_response_headers()` 返回的请求头,`strtok()` 状态,以及 `output_add_rewrite_var()` 添加的变量,但 **`$_ENV` 不会被重置**,并且 `putenv()` 写入、`static` 变量、类静态属性、全局变量和运行时引擎设置(`ini_set()`、默认流上下文、默认时区、工作目录等)会在同一线程的请求之间持久存在。留在该状态中的请求或用户特定数据可能会泄露到后续请求中(参见 [工作模式](worker.md#state-persistence))。
- **每线程环境沙盒**:`frankenphp_putenv()` / `frankenphp_getenv()` 操作的是线程本地的 `sandboxed_env`。`putenv()` 写入保留在该沙盒内,绝不会到达进程全局的操作系统环境,因此一个请求设置的秘密不会泄露给子进程(`proc_open`、`exec` 等)、Go/Caddy 端或其他请求。必须看到此类变量的子进程必须通过 `proc_open()` 或 `exec()` 的环境参数明确地提供给它(参见 [内部原理](internals.md#per-thread-environment-sandboxing))。
- **CGO 内存边界**:Go 字符串固定以及 `C.CString()` / `free()` 在 Go ↔ C 边界上的生命周期。
- **Caddy 管理 API**:`/frankenphp/workers/restart` 和 `/frankenphp/threads` 端点,通过 Caddy 的管理 API(默认监听 `localhost:2019`)暴露。将该端点暴露到 localhost 之外是操作员的决定。
- **受信任代理处理**:传入的 `X-Forwarded-*` 请求头总是作为不可信的 `$_SERVER['HTTP_X_FORWARDED_*']` 值到达 PHP;只有当配置了 [`trusted_proxies`](production.md#running-behind-a-reverse-proxy) 时,它们才被信任用于推导真实的客户端 IP 和协议。
- **慢速请求体**:客户端宣布请求体后,如果缓慢发送或停滞,会长时间占用处理线程。在一个有界线程池中,足够多的此类连接会耗尽线程池(慢速 POST DoS)。FrankenPHP 默认对请求体读取应用 60 秒的空闲超时([`request_body_timeout`](config.md#caddyfile-config)),并在每次读取前重置截止时间,这样任何大小的稳定上传都能成功,而停滞的上传会被切断并释放线程。

## 范围之外

- **应用程序 PHP 代码中的漏洞**(SQL 注入、XSS、不安全的反序列化等)。FrankenPHP 将不可信的请求数据原封不动地传递给应用程序;防御这些漏洞是应用程序的职责,就像任何 SAPI 一样。
- **FrankenPHP 使用的上游组件(PHP、Caddy、Go)或在其之上构建的项目(Laravel Octane、Symfony Runtime)中的缺陷。** 请将这些报告给相关项目。
- **应用程序 PHP 代码中的漏洞**(SQL 注入、XSS、不安全的反序列化等)。FrankenPHP 将不可信的请求数据原封不动地传递给应用程序;防御这些漏洞是应用程序的职责,就像任何 SAPI 一样。
- **FrankenPHP 使用的上游组件(PHP、Caddy、Go)或在其之上构建的项目(Laravel Octane、Symfony Runtime)中的缺陷。** 请将这些报告给相关项目。

## 报告漏洞

Expand Down
1 change: 1 addition & 0 deletions docs/cn/worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ $handler = static function () use ($workerServer) {
- **全局变量**:worker 脚本全局范围内的变量在请求之间持久化。
- **内存缓存**:在请求处理器之外存储在内存中的任何数据(数组、对象)都将持久化。
- **运行时引擎设置**:使用 `ini_set()`、`stream_context_set_default()`、`date_default_timezone_set()`、`chdir()`、`stream_wrapper_register()`、`set_error_handler()` 或 `set_exception_handler()` 进行的更改对后续请求仍然有效。切勿在此处存储与请求或用户相关的特定值,例如默认流上下文中的凭据:请为每个调用传递一个显式上下文。
- **进程范围设置**:`umask()` 和 `setlocale()` 会更改整个 FrankenPHP 进程,而不仅仅是当前线程。Worker 模式不会在请求之间恢复它们,并且在任何模式下它们也会影响同时运行的请求。请在启动时设置一次,或者对创建的文件调用 `chmod()` 而不是更改 umask。

这是设计使然,也是 worker 模式快速的原因。然而,它需要注意避免意外的副作用:

Expand Down
3 changes: 2 additions & 1 deletion docs/fr/worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ docker run \

Si votre application utilise [PSR-15](https://www.php-fig.org/psr/psr-15/) au lieu des superglobales, convertissez la requête/réponse aux limites du handler avec [`nyholm/psr7`](https://github.com/Nyholm/psr7) et [`nyholm/psr7-server`](https://github.com/Nyholm/psr7-server) :

```bash
```console
composer require nyholm/psr7 nyholm/psr7-server psr/http-server-handler
```

Expand Down Expand Up @@ -244,6 +244,7 @@ Parce que le mode worker maintient le processus PHP en vie entre les requêtes,
- **Variables globales** : Les variables dans la portée globale du script worker persistent entre les requêtes.
- **Caches en mémoire** : Toute donnée stockée en mémoire (tableaux, objets) en dehors du gestionnaire de requêtes persiste.
- **Paramètres du moteur d'exécution** : Les modifications effectuées avec `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` ou `set_exception_handler()` restent en vigueur pour les requêtes ultérieures. Ne stockez jamais de valeurs spécifiques à la requête ou à l'utilisateur, telles que des identifiants dans le contexte de flux par défaut : passez plutôt un contexte explicite à chaque appel.
- **Paramètres à l'échelle du processus** : `umask()` et `setlocale()` modifient l'ensemble du processus FrankenPHP, pas seulement le thread actuel. Le mode worker ne les restaure pas entre les requêtes, et dans tous les modes, ils affectent également les requêtes exécutées simultanément. Définissez-les une fois au démarrage, ou appelez `chmod()` sur les fichiers créés au lieu de modifier l'umask.

C'est un comportement intentionnel et c'est ce qui rend le mode worker rapide. Cependant, cela demande de l'attention pour éviter les effets secondaires involontaires :

Expand Down
37 changes: 19 additions & 18 deletions docs/it/worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ description: Esegui FrankenPHP in modalità worker per mantenere la tua applicaz

# Utilizzare i worker di FrankenPHP

Avviare l'applicazione una volta e mantenerla in memoria.
Avvia la tua applicazione una volta e mantienila in memoria.
FrankenPHP gestirà le richieste in arrivo in pochi millisecondi.

## Avvio degli script worker di FrankenPHP

### Esecuzione di un worker FrankenPHP con Docker

Impostare il valore della variabile di ambiente `FRANKENPHP_CONFIG` su `worker /path/to/your/worker/script.php`:
Imposta il valore della variabile di ambiente `FRANKENPHP_CONFIG` su `worker /path/to/your/worker/script.php`:

```bash
docker run \
Expand All @@ -24,35 +24,35 @@ docker run \

### Esecuzione di un worker FrankenPHP con il binario autonomo

Utilizzare l'opzione `--worker` del comando `php-server` per servire il contenuto della cartella corrente utilizzando un worker:
Usa l'opzione `--worker` del comando `php-server` per servire il contenuto della directory corrente utilizzando un worker:

```bash
frankenphp php-server --worker /path/to/your/worker/script.php
```

Se l'app PHP è [incorporata nel binario](embed.md), si può aggiungere un `Caddyfile` personalizzato nella cartella principale dell'app.
Se la tua app PHP è [incorporata nel binario](embed.md), puoi aggiungere un `Caddyfile` personalizzato nella directory radice dell'app.
Verrà utilizzato automaticamente.

È anche possibile [riavviare il worker in caso di modifiche al file](config.md#watching-for-file-changes) con l'opzione `--watch`.
Il seguente comando attiverà un riavvio se qualsiasi file che termina con `.php` nella cartella o nelle sottocartelle `/path/to/your/app/` viene modificato:
È anche possibile [riavviare il worker in caso di modifiche ai file](config.md#monitorare-le-modifiche-ai-file) con l'opzione `--watch`.
Il seguente comando attiverà un riavvio se qualsiasi file che termina con `.php` nella directory `/path/to/your/app/` o nelle sue sottodirectory viene modificato:

```bash
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
```

Questa funzione viene spesso utilizzata in combinazione con [ricaricamento a caldo](hot-reload.md).
Questa funzione viene spesso utilizzata in combinazione con il [ricaricamento a caldo](hot-reload.md).

## Modalità worker per Symfony

Vedi [la documentazione sulla modalità worker di FrankenPHP con Symfony](symfony.md#symfony-worker-mode-with-frankenphp).
Vedi [la documentazione sulla modalità worker di FrankenPHP con Symfony](symfony.md#symfony-worker-mode-con-frankenphp).

## Modalità worker per Laravel Octane

Vedi [la documentazione di FrankenPHP Laravel Octane](laravel.md#laravel-octane).

## Scrittura di uno script worker FrankenPHP personalizzato

L'esempio seguente mostra come creare il proprio script worker senza fare affidamento su una libreria di terze parti:
L'esempio seguente mostra come creare il tuo script worker senza fare affidamento su una libreria di terze parti:

```php
<?php
Expand All @@ -64,10 +64,10 @@ require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();

// Gestore fuori dal ciclo per migliori prestazioni (meno lavoro per richiesta)
// Gestore fuori dal ciclo per migliori prestazioni (meno lavoro)
$handler = static function () use ($myApp) {
try {
// Chiamato quando arriva una richiesta,
// Chiamato quando viene ricevuta una richiesta,
// i superglobali, php://input e simili vengono reimpostati
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
} catch (\Throwable $exception) {
Expand All @@ -84,7 +84,7 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests
// Esegui qualcosa dopo aver inviato la risposta HTTP
$myApp->terminate();

// Richiama il garbage collector per ridurre la probabilità che parta durante la generazione di una pagina
// Richiama il garbage collector per ridurre la probabilità che venga attivato durante la generazione di una pagina
gc_collect_cycles();

if (!$keepRunning) break;
Expand All @@ -94,7 +94,7 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests
$myApp->shutdown();
```

Quindi, avviare l'app e utilizzare la variabile di ambiente `FRANKENPHP_CONFIG` per configurare il worker:
Quindi, avvia la tua app e utilizza la variabile di ambiente `FRANKENPHP_CONFIG` per configurare il tuo worker:

```bash
docker run \
Expand Down Expand Up @@ -165,16 +165,16 @@ for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests

### Riavvia il worker dopo un certo numero di richieste

Poiché PHP non è stato originariamente progettato per processi di lunga durata, molte librerie e codici legacy perdono ancora memoria.
Poiché PHP non è stato originariamente progettato per processi di lunga durata, molte librerie e codice legacy perdono ancora memoria.
Una soluzione alternativa all'utilizzo di questo tipo di codice in modalità worker consiste nel riavviare lo script worker dopo aver elaborato un certo numero di richieste:

Il precedente script worker consente di configurare un numero massimo di richieste da gestire impostando una variabile di ambiente denominata `MAX_REQUESTS`.

### Riavvia i worker manualmente

Sebbene sia possibile riavviare i worker [in seguito alle modifiche dei file](config.md#watching-for-file-changes), è anche possibile riavviare tutti i worker
Sebbene sia possibile riavviare i worker [in seguito alle modifiche dei file](config.md#monitorare-le-modifiche-ai-file), è anche possibile riavviare tutti i worker
in modo elegante tramite l'[API di amministrazione Caddy](https://caddyserver.com/docs/api). Se l'amministratore è abilitato nel tuo
[Caddyfile](config.md#caddyfile-config), si può eseguire il ping dell'endpoint di riavvio con una semplice richiesta POST:
[Caddyfile](config.md#configurazione-caddyfile), puoi eseguire il ping dell'endpoint di riavvio con una semplice richiesta POST:

```bash
curl -X POST http://localhost:2019/frankenphp/workers/restart
Expand All @@ -183,12 +183,12 @@ curl -X POST http://localhost:2019/frankenphp/workers/restart
### Fallimenti dei worker

Se uno script worker si blocca con un codice di uscita diverso da zero, FrankenPHP lo riavvierà con una strategia di backoff esponenziale.
Se lo script worker rimane attivo più a lungo dell'ultimo backoff moltiplicato per due,
Se lo script worker rimane attivo più a lungo dell'ultimo backoff × 2,
non penalizzerà lo script worker e lo riavvierà di nuovo.
Tuttavia, se lo script worker continua a fallire con un codice di uscita diverso da zero in un breve periodo di tempo
(ad esempio, avendo un errore di battitura in uno script), FrankenPHP si bloccherà con l'errore: `too many consecutive failures`.

Il numero di errori consecutivi può essere configurato nel tuo [Caddyfile](config.md#caddyfile-config) con l'opzione `max_consecutive_failures`:
Il numero di errori consecutivi può essere configurato nel tuo [Caddyfile](config.md#configurazione-caddyfile) con l'opzione `max_consecutive_failures`:

```caddyfile
frankenphp {
Expand Down Expand Up @@ -243,6 +243,7 @@ Poiché la modalità worker mantiene attivo il processo PHP tra le richieste, il
- **Variabili globali**: le variabili nell'ambito globale dello script worker persistono tra le richieste.
- **Cache in memoria**: tutti i dati archiviati in memoria (array, oggetti) all'esterno del gestore delle richieste persistono.
- **Impostazioni del motore di runtime**: le modifiche apportate con `ini_set()`, `stream_context_set_default()`, `date_default_timezone_set()`, `chdir()`, `stream_wrapper_register()`, `set_error_handler()` o `set_exception_handler()` rimangono in vigore per le richieste successive. Non archiviare mai valori specifici della richiesta o dell'utente in queste impostazioni, come le credenziali nel contesto del flusso predefinito: passa invece un contesto esplicito ad ogni chiamata.
- **Impostazioni a livello di processo**: `umask()` e `setlocale()` modificano l'intero processo FrankenPHP, non solo il thread corrente. La modalità worker non li ripristina tra le richieste, e in ogni modalità influenzano anche le richieste in esecuzione contemporaneamente. Impostali una volta all'avvio, o chiama `chmod()` sui file creati invece di modificare la umask.

Questo è previsto dalla progettazione ed è ciò che rende veloce la modalità worker. Tuttavia, richiede attenzione per evitare effetti collaterali indesiderati:

Expand Down
Loading
Loading