Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f262d89d | ||
|
|
1ddb97371f | ||
| eb756ad3b1 |
47
HANDOFF.md
47
HANDOFF.md
@@ -4,7 +4,52 @@ Stato del progetto per chi ci mette mano dopo. Per installazione, deploy e uso
|
||||
quotidiano vedi [README.md](README.md); qui c'è il contesto che il README non
|
||||
può dare: perché le cose sono come sono, e cosa resta aperto.
|
||||
|
||||
Ultimo aggiornamento: **12 ottobre 2026** — vedi [Lavori in corso](#lavori-in-corso--12-ottobre-2026-riprendere-da-qui).
|
||||
Ultimo aggiornamento: **21 ottobre 2026** (rilascio di sicurezza di PHP) — vedi [Sicurezza: PHP](#sicurezza-php-cve-2026-109618--cve-2026-109619-21-ottobre-2026).
|
||||
|
||||
> **Questo è l'HANDOFF di `main` (produzione).** Il 21/10/2026 in produzione
|
||||
> è andata **solo la correzione di PHP**, scelta dell'utente: le funzioni nuove
|
||||
> di `dev` (gruppi di servizio e lavori del 20 ottobre) restano sulla demo.
|
||||
> Il quadro completo e aggiornato del progetto è nell'HANDOFF di `dev`, che
|
||||
> sostituirà questo quando `dev` verrà portato in produzione.
|
||||
|
||||
---
|
||||
|
||||
## Sicurezza: PHP CVE-2026-109618 / CVE-2026-109619 (21 ottobre 2026)
|
||||
|
||||
Due esecuzioni di codice da remoto nel core di PHP (CVSS v4 9.2), patch alle
|
||||
14:00 UTC del 21/10/2026; versione corretta per il nostro ramo: **8.3.36**.
|
||||
La produzione girava su 8.3.35. Rilasciati su `main` solo i due commit di
|
||||
sicurezza di `dev` (cherry-pick):
|
||||
|
||||
- `docker/php/Dockerfile`: `ARG PHP_VERSION=8.3.36` e `FROM
|
||||
php:${PHP_VERSION}-fpm`. Prima era `php:8.3-fpm`, un tag che si sposta ma
|
||||
che Docker tiene in cache: una ricostruzione senza `--pull` (anche su
|
||||
Dokploy) poteva riusare la base vulnerabile. Ora una build o prende la
|
||||
8.3.36 o fallisce. Test `tests/Unit/VersionePhpTest.php` contro il ritorno a
|
||||
un tag mobile o a una versione inferiore.
|
||||
- phpredis da GitHub (`ARG PHPREDIS_VERSION=6.3.0`) invece di `pecl install`:
|
||||
quel giorno pecl.php.net rispondeva 504 e la build falliva.
|
||||
- `docker/php/php.ini`: `session.upload_progress.enabled = 0` e
|
||||
`session.use_strict_mode = 1`, la mitigazione indicata da Sansec in attesa
|
||||
della patch, da tenere anche dopo (l'applicazione usa le sessioni di
|
||||
Laravel, in Redis).
|
||||
- Solo per provare in locale prima che l'immagine sia su Docker Hub:
|
||||
`PHP_VERSION=8.3.35 docker compose ...` (mai per un deploy).
|
||||
|
||||
**In produzione dal 21 ottobre 2026, ore 18:48** (16:48 UTC; `main` =
|
||||
`1ddb973`). Su Docker Hub il tag `php:8.3.36-fpm` è comparso alle 15:38 UTC
|
||||
ma vuoto: l'immagine per amd64 (il server) è arrivata alle 16:41 UTC, e prima
|
||||
la build falliva con "no match for platform in manifest". Prima del deploy:
|
||||
test di questa versione su PHP 8.3.36 (524 PHP + 13 JS verdi) e backup
|
||||
`/etc/dokploy/termanager2-backups/prod-20261021-164333` (database, 78 MB di
|
||||
`storage/app`, conteggi). Deploy lanciato a mano (l'autodeploy non è
|
||||
partito), uno solo. Verifica: PHP 8.3.36, `session.upload_progress.enabled`
|
||||
Off e `session.use_strict_mode` On dal `php.ini` dell'immagine, nessuna
|
||||
migrazione in sospeso, conteggi identici al backup (274 territori, 1130
|
||||
assegnazioni di cui 77 aperte, 111 proclamatori, 4 utenti, 1251 righe di log,
|
||||
35 migrazioni), login 200, container sani, nessun errore nei log. La
|
||||
mitigazione applicata a mano ai container la mattina (in `conf.d`) è
|
||||
sparita con la ricostruzione, come previsto: vale il `php.ini`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -812,6 +812,13 @@ una revoca ha effetto immediato senza bisogno di ricaricare la pagina.
|
||||
- **Sessioni**: in Redis, cookie httpOnly e `Secure` in produzione
|
||||
- **Esecuzione PHP**: solo `index.php` è un entrypoint valido; ogni altro `.php`
|
||||
restituisce 404
|
||||
- **Versione di PHP fissata alla patch** (`ARG PHP_VERSION` nel Dockerfile,
|
||||
oggi 8.3.36 per CVE-2026-109618/109619): una build o prende quella versione
|
||||
o fallisce, mai una base vecchia rimasta in cache. Va alzata a ogni rilascio
|
||||
di sicurezza del ramo 8.3
|
||||
- **Sessioni native di PHP chiuse**: `session.upload_progress.enabled = 0` e
|
||||
`session.use_strict_mode = 1` in `php.ini` (l'applicazione usa le sessioni
|
||||
di Laravel; è la mitigazione indicata per le stesse CVE)
|
||||
|
||||
### GDPR
|
||||
- **Informativa** (menu *Privacy*, solo amministratore): i valori che possono
|
||||
@@ -906,7 +913,7 @@ ancora rientrati compaiono nel dettaglio come "in corso".
|
||||
| Componente | Tecnologia |
|
||||
|-------------------|--------------------------------------------|
|
||||
| Framework | Laravel 11 |
|
||||
| PHP | 8.3 (FPM) |
|
||||
| PHP | 8.3 (FPM), versione fissata nel Dockerfile |
|
||||
| Interfaccia | Livewire 3.5 + TailwindCSS 4 + Alpine.js |
|
||||
| Database | MariaDB 11 |
|
||||
| Cache/Sessioni/Code| Redis 7 |
|
||||
|
||||
@@ -18,6 +18,10 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
# Vuota = la versione fissata nel Dockerfile (ARG PHP_VERSION). Si
|
||||
# imposta solo per provare in locale una versione diversa.
|
||||
args:
|
||||
- PHP_VERSION
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
environment:
|
||||
@@ -46,6 +50,10 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
# Vuota = la versione fissata nel Dockerfile (ARG PHP_VERSION). Si
|
||||
# imposta solo per provare in locale una versione diversa.
|
||||
args:
|
||||
- PHP_VERSION
|
||||
restart: unless-stopped
|
||||
user: www-data
|
||||
env_file: .env
|
||||
@@ -89,6 +97,10 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
# Vuota = la versione fissata nel Dockerfile (ARG PHP_VERSION). Si
|
||||
# imposta solo per provare in locale una versione diversa.
|
||||
args:
|
||||
- PHP_VERSION
|
||||
restart: unless-stopped
|
||||
user: www-data
|
||||
env_file: .env
|
||||
@@ -207,6 +219,10 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/php/Dockerfile
|
||||
# Vuota = la versione fissata nel Dockerfile (ARG PHP_VERSION). Si
|
||||
# imposta solo per provare in locale una versione diversa.
|
||||
args:
|
||||
- PHP_VERSION
|
||||
# Senza questo si costruirebbe l'immagine di produzione, che essendo
|
||||
# installata senza dipendenze di sviluppo non contiene phpunit.
|
||||
target: test
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Versione di PHP, fissata alla patch. Va dichiarata prima del primo FROM per
|
||||
# poterla usare nel FROM dello stage `base`.
|
||||
#
|
||||
# Fissata e non "8.3": quel tag si sposta, ma Docker tiene in cache la base già
|
||||
# scaricata, e una ricostruzione senza --pull (anche su Dokploy) poteva uscire
|
||||
# con un PHP vecchio senza che nessuno se ne accorgesse. Con la patch scritta
|
||||
# qui, una build o prende questa versione o fallisce.
|
||||
#
|
||||
# 8.3.36: corregge CVE-2026-109618 e CVE-2026-109619 (esecuzione di codice da
|
||||
# remoto nel core di PHP, CVSS 9.2, rilascio del 21/10/2026). Non scendere
|
||||
# sotto. Aggiornala a ogni rilascio di sicurezza del ramo 8.3.
|
||||
#
|
||||
# Solo per provare in locale prima che l'immagine corretta sia su Docker Hub:
|
||||
# PHP_VERSION=8.3.35 docker compose ... (mai per un deploy).
|
||||
ARG PHP_VERSION=8.3.36
|
||||
|
||||
# =============================================================================
|
||||
# Stage 1 — PHP dependencies (no dev, production autoloader)
|
||||
# =============================================================================
|
||||
@@ -65,7 +81,7 @@ RUN npm run build
|
||||
# estensioni e sullo stesso php.ini della produzione: un test che passa qui
|
||||
# non puo' passare grazie a un ambiente diverso da quello vero.
|
||||
# =============================================================================
|
||||
FROM php:8.3-fpm AS base
|
||||
FROM php:${PHP_VERSION}-fpm AS base
|
||||
|
||||
# nginx + supervisor run in-image alongside php-fpm: a separate nginx container
|
||||
# would need its own copy of public/ plus the storage volume just to serve
|
||||
@@ -76,6 +92,11 @@ FROM php:8.3-fpm AS base
|
||||
# are named per Debian release (libicu72 vs libicu76, libzip4 vs libzip5), so
|
||||
# purging means pinning package names that break whenever the php base image
|
||||
# moves to a new Debian. A few tens of MB is worth not having that breakage.
|
||||
#
|
||||
# phpredis si scarica da GitHub, versione fissata, e non con `pecl install`:
|
||||
# il 21/10/2026, giorno della patch di sicurezza di PHP, pecl.php.net
|
||||
# rispondeva 504 e la build falliva proprio quando bisognava aggiornare.
|
||||
ARG PHPREDIS_VERSION=6.3.0
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
netcat-openbsd \
|
||||
@@ -101,8 +122,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
intl \
|
||||
zip \
|
||||
opcache \
|
||||
&& pecl install redis \
|
||||
&& docker-php-ext-enable redis \
|
||||
&& mkdir -p /usr/src/php/ext/redis \
|
||||
&& curl -fsSL --retry 5 --retry-delay 5 \
|
||||
"https://github.com/phpredis/phpredis/archive/refs/tags/${PHPREDIS_VERSION}.tar.gz" \
|
||||
| tar xz --strip-components=1 -C /usr/src/php/ext/redis \
|
||||
&& echo redis >> /usr/src/php-available-exts \
|
||||
&& docker-php-ext-install -j"$(nproc)" redis \
|
||||
&& docker-php-source delete \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -8,6 +8,16 @@ memory_limit = 256M
|
||||
max_execution_time = 120
|
||||
max_input_vars = 3000
|
||||
|
||||
[Session]
|
||||
; Mitigazione di CVE-2026-109618/109619 (RCE nel core di PHP, 21/10/2026),
|
||||
; come indicato da Sansec in attesa della patch, e da tenere anche dopo:
|
||||
; l'avanzamento degli upload nelle sessioni native di PHP non serve
|
||||
; all'applicazione (Laravel ha le sue sessioni, in Redis, e Livewire non lo
|
||||
; usa) ed è il punto da cui passano questi attacchi. Lo strict mode vale solo
|
||||
; per le sessioni native, che qui non si usano: costa niente.
|
||||
session.upload_progress.enabled = 0
|
||||
session.use_strict_mode = 1
|
||||
|
||||
[opcache]
|
||||
opcache.enable = 1
|
||||
opcache.memory_consumption = 128
|
||||
|
||||
54
tests/Unit/VersionePhpTest.php
Normal file
54
tests/Unit/VersionePhpTest.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* L'immagine deve partire da una versione di PHP fissata alla patch, e mai
|
||||
* sotto quella che corregge CVE-2026-109618 e CVE-2026-109619 (esecuzione di
|
||||
* codice da remoto nel core di PHP, 21/10/2026). Con un tag che si sposta
|
||||
* ("8.3-fpm") la cache di Docker poteva riusare una base vulnerabile.
|
||||
*/
|
||||
class VersionePhpTest extends TestCase
|
||||
{
|
||||
private const MINIMA = '8.3.36';
|
||||
|
||||
private function dockerfile(): string
|
||||
{
|
||||
return (string) file_get_contents(dirname(__DIR__, 2) . '/docker/php/Dockerfile');
|
||||
}
|
||||
|
||||
public function test_la_versione_di_php_e_fissata_e_corretta(): void
|
||||
{
|
||||
// \s* prima di $: nella copia di lavoro su Windows il file ha CRLF.
|
||||
$this->assertMatchesRegularExpression('/^ARG PHP_VERSION=(\d+\.\d+\.\d+)\s*$/m', $this->dockerfile());
|
||||
|
||||
preg_match('/^ARG PHP_VERSION=(\d+\.\d+\.\d+)\s*$/m', $this->dockerfile(), $trovata);
|
||||
|
||||
$this->assertTrue(
|
||||
version_compare($trovata[1], self::MINIMA, '>='),
|
||||
"PHP {$trovata[1]} è vulnerabile a CVE-2026-109618/109619: serve almeno " . self::MINIMA . '.',
|
||||
);
|
||||
}
|
||||
|
||||
public function test_la_mitigazione_delle_sessioni_resta_nel_php_ini(): void
|
||||
{
|
||||
// Indicata da Sansec per CVE-2026-109618/109619; all'applicazione non
|
||||
// serve, quindi resta anche dopo la patch.
|
||||
$ini = (string) file_get_contents(dirname(__DIR__, 2) . '/docker/php/php.ini');
|
||||
|
||||
$this->assertMatchesRegularExpression('/^session\.upload_progress\.enabled\s*=\s*0\s*$/m', $ini);
|
||||
$this->assertMatchesRegularExpression('/^session\.use_strict_mode\s*=\s*1\s*$/m', $ini);
|
||||
}
|
||||
|
||||
public function test_l_immagine_usa_la_versione_fissata_e_non_un_tag_mobile(): void
|
||||
{
|
||||
preg_match_all('/^FROM php:(\S+)/m', $this->dockerfile(), $basi);
|
||||
|
||||
$this->assertNotEmpty($basi[1]);
|
||||
foreach ($basi[1] as $tag) {
|
||||
$this->assertStringStartsWith('${PHP_VERSION}', $tag, "FROM php:{$tag}: usa ARG PHP_VERSION, non un tag che si sposta.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user