Quan surt una vulnerabilitat crítica de PHP, actualitzar sol ser tan fàcil com executar l’actualitzador de la distribució. Però, què passa quan el servidor afectat encara depèn d’una versió de PHP que ja no rep pedaços oficials en format de paquet, i l’aplicació que hi corre no és compatible amb cap versió moderna?
En aquest article expliquem, de manera genèrica i reproduïble, com vam corregir dues vulnerabilitats crítiques de PHP (execució de codi remot sense autenticació) en un servidor que havia de continuar funcionant amb PHP 7.4, compilant el pedaç oficial des del codi font i substituint només la peça vulnerable.
Avís: compilar i substituir components de PHP a mà és l’últim recurs, per a casos en què no hi ha paquet oficial i no es pot migrar de versió a curt termini. Sempre que existeixi un paquet oficial, feu servir el paquet. I sempre, sempre, proveu-ho abans en una còpia i tingueu una via de marxa enrere.
El context del problema
L’escenari era aquest:
- Una aplicació antiga (un CRM heretat) que només funciona amb PHP 7.4 i que no es pot migrar a PHP 8 a curt termini.
- PHP 7.4 està en final de vida: l’equip de PHP i els repositoris de tercers ja no publiquen paquets nous per a aquesta branca.
- Davant d’una vulnerabilitat crítica, l’equip de seguretat de PHP sí que va publicar el codi font del pedaç per a les versions antigues, pensat precisament per a qui compila des de codi.
L’objectiu: aplicar aquest pedaç al servidor en producció amb el mínim risc.
La idea clau: substituir només el mòdul, no reinstal·lar PHP
En una instal·lació típica amb Apache i mod_php, PHP s’executa dins de l’Apache com un mòdul: un únic fitxer (libphp7.4.so). La vulnerabilitat era al nucli de PHP, que és dins d’aquest mòdul.
Per tant, no cal recompilar tot PHP ni tocar les extensions (base de dades, imatges, correu, etc.), que són fitxers separats. N’hi ha prou amb compilar un mòdul nou amb el pedaç i substituir-lo, mantenint la resta igual.


Pas 1: recollir la «recepta» de la instal·lació actual
Abans de res, cal saber exactament com està compilat el PHP que hi ha instal·lat, per reproduir-lo de manera idèntica. Les dades importants són:
- La versió exacta i els números d’API de PHP.
- Si està compilat amb Thread Safety o sense (NTS).
- Les opcions de compilació (
./configure). - Quines extensions van dins del nucli i quines són fitxers externs.
La versió i l’arquitectura es poden consultar així:
php -i | grep -E "^(PHP Version|PHP API|Thread Safety|extension_dir)"
Les opcions de compilació, si teniu el paquet -dev instal·lat:
php-config --configure-options
I feu una còpia del mòdul actual, que servirà de referència i de marxa enrere:
cp /usr/lib/apache2/modules/libphp7.4.so /root/libphp7.4.so.original
Pas 2: compilar en un entorn aïllat i idèntic
Compilar directament al servidor de producció és una mala idea: consumeix recursos i deixa eines de desenvolupament instal·lades. La solució neta és compilar en un contenidor amb el mateix sistema operatiu que el servidor. Així el resultat és compatible, perquè dins del contenidor reproduïm les mateixes biblioteques que té el servidor.
# Contenidor amb el mateix SO que el servidor
docker run -it --name build -v "$PWD":/out ubuntu:XX.XX bash
Dins del contenidor, instal·lem les eines de compilació i les biblioteques de desenvolupament que PHP necessita (XML, SSL, zlib, etc.):
apt-get update
apt-get install -y build-essential autoconf bison re2c pkg-config git \
apache2-dev libxml2-dev libssl-dev libpcre2-dev zlib1g-dev \
libargon2-dev libsodium-dev
Pas 3: descarregar el codi font oficial amb el pedaç
El punt més important de tot el procés: el codi ha de venir de la font oficial. L’equip de seguretat de PHP publica el pedaç al repositori oficial de PHP, normalment en una branca específica per a la vulnerabilitat, i sempre l’enllaça des de l’avís oficial de php.net.
git clone --depth 1 -b <branca-oficial-del-pedac> https://github.com/php/php-src.git
cd php-src
Verifiqueu que el codi és el que ha de ser. Comproveu la diferència respecte de la versió oficial que teniu instal·lada: només haurien de canviar els fitxers del pedaç, no centenars de fitxers.
git fetch --depth 1 origin tag php-7.4.XX
git diff --stat php-7.4.XX HEAD
Si la diferència són uns pocs fitxers del nucli (gestió de memòria, processament de peticions…), és el pedaç mínim sobre la vostra versió. Si en surten centenars, la branca no correspon i cal partir de la versió oficial exacta i aplicar-hi només el pedaç.
Pas 4: compilar amb la mateixa configuració
Es fa servir la mateixa recepta del pas 1, amb aquestes condicions:
- Conservar les rutes de configuració (
--with-config-file-path, etc.). - Generar el mòdul d’Apache amb
--with-apxs2. - Mantenir el mateix mode de Thread Safety (per a Apache prefork, NTS).
./buildconf --force
./configure <les-mateixes-opcions-que-el-paquet-original> --with-apxs2=/usr/bin/apxs2
make -j"$(nproc)"
Abans de donar-ho per bo, comproveu que la configuració ha deixat les mateixes extensions internes que el mòdul original i que no activa Thread Safety si no toca.
Pas 5: provar el mòdul abans de posar-lo en producció
Dues comprovacions valuoses:
a) Les proves oficials del codi. El codi de PHP porta milers de proves automàtiques. Executeu-les per confirmar que el mòdul compilat és sòlid:
make test
b) Una instància d’Apache de prova. Abans de tocar el servei real, es pot aixecar un Apache de test en un port intern que carregui el mòdul nou amb la configuració, les extensions i els complements reals, i comprovar que la versió és correcta, que carrega totes les extensions i que connecta amb la base de dades. Tot això sense tocar el servei en producció.
Pas 6: desplegar i, sobretot, poder tornar enrere
Primer, traieu del mòdul la informació de depuració perquè quedi d’una mida semblant a l’original:
strip --strip-unneeded libs/libphp7.so -o libphp7.4.so.patched
Copieu el mòdul al servidor i comproveu-ne la integritat amb l’empremta (SHA-256), per assegurar que ha arribat sencer:
sha256sum libphp7.4.so.patched
El desplegament es fa de manera que l’actualitzador de paquets no torni a sobreescriure el nostre mòdul. En sistemes basats en Debian/Ubuntu, això s’aconsegueix amb dpkg-divert, que «aparta» el fitxer original i deixa el nostre al seu lloc:
dpkg-divert --local --rename --add /usr/lib/apache2/modules/libphp7.4.so
install -m 644 libphp7.4.so.patched /usr/lib/apache2/modules/libphp7.4.so
apache2ctl configtest && systemctl restart apache2
Marxa enrere en un minut, si cal:
rm /usr/lib/apache2/modules/libphp7.4.so
dpkg-divert --local --rename --remove /usr/lib/apache2/modules/libphp7.4.so
systemctl restart apache2

Pas 7: verificar que el servei fa servir el mòdul nou
El número de versió no canvia (continua sent 7.4.XX), així que cal confirmar-ho d’altres maneres:
- La data de compilació (
Build Dated’unphpinfo) ha de ser la del vostre desplegament, no l’original. - L’empremta del mòdul instal·lat ha de coincidir amb la que vau calcular.
- Cap procés del servidor web ha de tenir el mòdul antic carregat a memòria (cal reiniciar el servei, no només recarregar-lo).
Un efecte col·lateral important: els escàners de vulnerabilitats
Com que el número de versió no canvia, els escàners de vulnerabilitats (que sovint només llegeixen la versió) continuaran marcant el servidor com a vulnerable. És un fals positiu. Convé documentar el procés (origen del codi, integritat, empremta del mòdul desplegat) per poder justificar-ho en una auditoria. És exactament el mateix criteri amb què es validen els pedaços retroportats de les distribucions: no es comprova l’atac a cada servidor, es verifica que el codi corregit és el que s’executa.

Mesures complementàries mentre dura la situació
Compilar el pedaç resol la vulnerabilitat, però un servidor amb PHP i sistema operatiu fora de suport continua sent un deute tècnic. Mentre no es pugui migrar, val la pena acompanyar-ho de:
- Les mitigacions oficials que recomani l’avís de seguretat (sovint, desactivar funcionalitats concretes i limitar la mida de les peticions).
- Reduir l’exposició: limitar l’accés a la xarxa interna o per VPN, i tancar eines d’administració sensibles darrere d’autenticació addicional.
- Un pla de migració real, perquè aquesta solució és un pont, no una destinació.
Conclusió
Quan una vulnerabilitat crítica afecta una versió de PHP sense suport, no esteu atrapats entre «deixar-ho obert» i «trencar l’aplicació». Si l’equip de PHP publica el codi del pedaç, es pot compilar un mòdul corregit, provar-lo en una còpia i desplegar-lo amb una via de marxa enrere clara, mantenint la versió i l’aplicació intactes.
No és el camí ideal (l’ideal és migrar a una versió amb suport), però és una manera responsable de guanyar el temps necessari per fer la migració amb garanties, sense deixar un forat crític obert mentrestant.
