Trumpai: open_basedir apriboja katalogus, prie kurių PHP gali kreiptis. Vienas jis yra tik papildomas barjeras. Veiksminga izoliacija kiekvienai svetainei atsiranda tik kartu: savas sistemos naudotojas, savas PHP-FPM baseinas, savas laikinų failų aplankas ir savos duomenų bazės prieigos. Tuomet įsilaužimas į vieną svetainę lieka toje svetainėje.
Kodėl po PHP2Shell izoliacija lemia žalą
Daugelyje serverių greta veikia kelios svetainės: įmonės svetainė, parduotuvė, sena kampanijos svetainė, o agentūrose dažnai dešimtys klientų projektų. Dažnai jos dalijasi vienu sistemos naudotoju ir vienu PHP procesu. Jei viena kompromituojama, visos kitos atviros.
PHP2Shell šią riziką padaro apčiuopiamą. Pažeidžiamumai CVE-2026-109618 ir CVE-2026-109619 leidžia vykdyti kenkėjišką kodą be prisijungimo, nepriklausomai nuo CMS. Pasak žiniasklaidos pranešimų, pirmasis atakos bandymas buvo jau 2026-10-17, o pataisos pasirodė spalio 21 d. Sprendimas yra atnaujinimas. Bet ar ataka tuo prieš tai laikotarpiu paliečia vieną ar dešimt svetainių, lemia izoliacija.
Ypač dėl antrojo pažeidžiamumo atsiranda dar viena mintis: pasak PHP projekto, vidinė užklausos būsena gali išlikti darbiniame procese (worker) ir būti panaudota vėlesnės užklausos tame pačiame darbiniame procese. Atskiri baseinai kiekvienai svetainei užtikrina, kad svetainės darbinių procesų vis tiek nesidalija.
Ką daro open_basedir
Su open_basedir nustatote katalogų sąrašą. PHP failų funkcijos, tokios kaip fopen, include ar file_get_contents, tuomet atsisako prieigos prie visko už šio sąrašo ribų. Įterptas skriptas taip negali be didesnių kliūčių skaityti kaimyninės svetainės konfigūracijos failų.
Ribos svarbios: patikra vyksta PHP viduje. Programoms, paleistoms už PHP ribų, ji netaikoma, o interpretatoriaus klaidos gali ją apeiti. Todėl open_basedir visada turi derėti su užblokuotomis sistemos funkcijomis, kaip aprašyta vadove Kaip teisingai naudoti disable_functions, ir visų pirma su atskyrimu operacinės sistemos lygiu.
Izoliacija kiekvienai svetainei keturiais sluoksniais
Patikimas atskyrimas remiasi keliais sluoksniais. Kiekvienas sustabdo tai, ko ankstesnis nepadaro.
| Sluoksnis | Priemonė | Poveikis |
|---|---|---|
| Operacinė sistema | savas sistemos naudotojas kiekvienai svetainei | Kitų svetainių failų skaityti negalima |
| PHP procesai | savas PHP-FPM baseinas su savu lizdu (socket) | Nėra bendro darbinio proceso, savi nustatymai |
| PHP konfigūracija | open_basedir, savas laikinų failų ir sesijų aplankas |
papildomas barjeras, jokių bendrų laikinų failų |
| Duomenys | savas duomenų bazės naudotojas su teisėmis tik į savo duomenų bazę | Jokios prieigos prie svetimų klientų duomenų |
Esant dideliam apsaugos poreikiui, pavyzdžiui, parduotuvėse su mokėjimų apdorojimu ar portaluose su sveikatos duomenimis, prisideda konteineriai ar atskiri serveriai.
open_basedir konfigūravimas PHP-FPM baseine
Toliau pateiktas pavyzdys rodo baseiną svetainei su savu naudotoju seite1. Kelius ir versijos numerį pritaikykite savo sistemai.
; /etc/php/8.3/fpm/pool.d/seite1.conf
[seite1]
user = seite1
group = seite1
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
php_admin_value[open_basedir] = /var/www/seite1/:/var/www/seite1-tmp/
php_admin_value[upload_tmp_dir] = /var/www/seite1-tmp/
php_admin_value[sys_temp_dir] = /var/www/seite1-tmp/
php_admin_value[session.save_path] = /var/www/seite1-tmp/sessions
php_admin_value[error_log] = /var/log/php/seite1.error.log
php_admin_flag[session.upload_progress.enabled] = off
Paskutinė eilutė kartu nustato php.net pereinamąją PHP2Shell priemonę. Ji veikia tik iš baseino, php.ini ar serverio konfigūracijos, o ne per ini_set() ar .user.ini. Aplankus sukurkite su tinkamomis teisėmis:
sudo useradd --system --home /var/www/seite1 --shell /usr/sbin/nologin seite1
sudo mkdir -p /var/www/seite1-tmp/sessions
sudo chown -R seite1:seite1 /var/www/seite1-tmp
sudo chmod 700 /var/www/seite1-tmp
Kaip turėtų atrodyti teisės pačiame žiniatinklio kataloge, aprašo vadovas Failų teisės žiniatinklio serveryje.
Žiniatinklio serverio prijungimas ir rezultato patikra
Nginx kiekviena svetainė nurodo savo lizdą. Taip jokia užklausa nepatenka į kitos svetainės baseiną.
server {
server_name seite1.example;
root /var/www/seite1/public;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/seite1.sock;
}
}
Daugiau patarimų šiam deriniui rasite puslapyje Nginx su PHP-FPM apsauga. Po paleidimo iš naujo patikrinkite, ar baseinai tikrai veikia atskirai:
sudo systemctl restart php8.3-fpm
# Kiekvienas baseinas turėtų veikti savo naudotoju
ps -eo user,pid,cmd | grep "[p]hp-fpm: pool"
Po to išbandykite visas svetainės funkcijas: įkėlimus, paveikslėlių apdorojimą, talpyklas, suplanuotas užduotis. Klaidų žurnalo pranešimai su nuoroda į open_basedir restriction parodo, kurio kelio dar trūksta. Pridėkite tik kelius, kurių programai tikrai reikia, ir niekada visos failų sistemos.
Agentūros su daugybe klientų svetainių baseinų konfigūraciją turėtų prižiūrėti kaip šabloną. Taip kiekviena nauja svetainė atskyrimą gauna automatiškai, o ne vėliau jį reikia pritaikyti. Pagrindą tam atvejui rasite puslapyje Saugumo partneris agentūroms.
Tipinės kliūtys pereinant
Perėjimas prie atskirų baseinų planuojamas, bet turi keletą pasikartojančių spąstų. Dažniausias susijęs su bendrai naudojamomis bibliotekomis: jei, pavyzdžiui, karkasas (framework) ar šriftų aplankas yra už svetainės katalogo ribų, šį kelią reikia įtraukti į open_basedir tik skaitymui. Geriau tokias priklausomybes perkelti į patį projektą.
Antrieji spąstai yra sesijos. Pasikeitus saugojimo vietai, visi lankytojai vieną kartą atjungiami, o vykstantys prekių krepšeliai prarandami. Todėl perėjimą planuokite ramiu laiku ir paskelbkite jį viduje.
Trečia, suplanuotos užduotys dažnai vykdomos per komandinę eilutę, taigi kitu naudotoju nei svetainė. Patikrinkite cron užduotis ir eiles ir leiskite joms veikti naujo svetainės naudotojo teisėmis, kitaip atsiranda failų su netinkamu savininku, kurių PHP vėliau nebegali keisti.
Galiausiai kiekvienas baseinas turėtų turėti savas procesų ir atminties ribas. Svetainė, kuriai kyla apkrova ar kuri atakuojama, taip negali parklupdyti kitų.
Kaip PHP SOS padeda konkrečiai?
- Nustatome, kurios svetainės šiandien dalijasi naudotojais, procesais ir duomenų bazėmis.
- Kiekvienai svetainei įrengiame savus naudotojus, baseinus, laikinų failų aplankus ir duomenų bazės prieigas, nekeisdami programų.
- Po perėjimo išbandome kiekvieną svetainę ir tikslingai ištaisome trūkstamus kelius.
- Atskyrimą dokumentuojame kaip šabloną būsimiems projektams.
Kaip tai telpa į bendrą koncepciją, aprašo paslauga PHP žiniatinklio serverių sutvirtinimas.
Dažni klausimai
Ar open_basedir pakanka svetainėms atskirti?
Ne. open_basedir yra papildomas barjeras PHP viduje, o ne tvirtas atskyrimas. Patikima izoliacija atsiranda tik su atskirais sistemos naudotojais ir baseinais kiekvienai svetainei, o esant didesniam apsaugos poreikiui su konteineriais ar atskirais serveriais.
Kodėl nustačius open_basedir nebeveikia įkėlimai?
Dažniausiai sąraše trūksta aplanko laikiniems failams. Kiekvienai svetainei sukurkite savo laikinų failų aplanką, įrašykite jį į open_basedir ir nustatykite upload_tmp_dir bei sys_temp_dir į šį aplanką.
Ar programa pati gali panaikinti open_basedir?
Programa vykdymo metu nustatymą gali tik dar labiau apriboti, bet ne sušvelninti. Vis tiek nustatykite open_basedir per php_admin_value baseine, kad reikšmė būtų vienareikšmiškai užfiksuota.
Ar izoliacija įmanoma ir bendrinamame hostinge?
Tai priklauso nuo hostingo teikėjo. Daugelis pasiūlymų klientus atskiria savais naudotojais ir PHP procesais. Paklauskite, kaip atskyrimas įgyvendintas, ypač kai keletas jūsų svetainių yra viename pakete.