Lyhyesti: open_basedir rajoittaa hakemistoja, joihin PHP saa päästä. Yksinään se on vain lisäeste. Tehokas sivustokohtainen eristys syntyy vasta yhteisvaikutuksesta: oma järjestelmäkäyttäjä, oma PHP-FPM-pooli, oma tmp-kansio ja omat tietokantatunnukset. Silloin yhteen verkkosivustoon tehty murto jää rajatuksi kyseiseen sivustoon.
Miksi eristys ratkaisee vahingon laajuuden PHP2Shellin jälkeen
Monella palvelimella toimii useita verkkosivustoja rinnakkain: yrityksen sivusto, verkkokauppa, vanha kampanjasivusto, toimistoilla usein kymmeniä asiakasprojekteja. Usein ne jakavat järjestelmäkäyttäjän ja PHP-prosessin. Kun yksi niistä vaarantuu, kaikki muut ovat avoinna.
PHP2Shell tekee tämän riskin konkreettiseksi. Haavoittuvuudet CVE-2026-109618 ja CVE-2026-109619 sallivat haittakoodin suorittamisen ilman kirjautumista CMS:stä riippumatta. Mediaraporttien mukaan ensimmäinen hyökkäysyritys tapahtui jo 17.10.2026, ja korjaukset tulivat 21.10. Korjaus on päivitys. Se, osuuko hyökkäys sitä edeltävänä aikana yhteen vai kymmeneen verkkosivustoon, ratkeaa kuitenkin eristyksessä.
Toisessa haavoittuvuudessa tulee vielä yksi ajatus: PHP-projektin mukaan pyynnön sisäinen tila voi säilyä workerissa ja myöhempi pyyntö samassa workerissa voi hyödyntää sitä. Sivustokohtaiset poolit varmistavat, etteivät verkkosivustot joka tapauksessa jaa workereita.
Mitä open_basedir tekee
open_basedir-asetuksella määritätte hakemistoluettelon. PHP:n tiedostofunktiot kuten fopen, include tai file_get_contents kieltäytyvät sen jälkeen pääsystä kaikkeen luettelon ulkopuolella. Lisätty komentosarja ei näin voi vaivatta lukea naapurisivuston asetustiedostoja.
Rajat ovat tärkeitä: tarkistus tapahtuu PHP:n sisällä. PHP:n ulkopuolella käynnistetyt ohjelmat eivät kuulu sen piiriin, ja tulkin virheet voivat kiertää sen. Siksi open_basedir kuuluu aina yhteen estettyjen järjestelmäfunktioiden kanssa, kuten kuvataan oppaassa disable_functions oikein käytettynä, ja ennen kaikkea käyttöjärjestelmätason erotukseen.
Sivustokohtainen eristys neljässä kerroksessa
Kestävä erotus rakentuu useista kerroksista. Kukin kerros pysäyttää sen, mihin edellinen ei yllä.
| Kerros | Toimi | Vaikutus |
|---|---|---|
| Käyttöjärjestelmä | oma järjestelmäkäyttäjä kutakin sivustoa kohden | Muiden sivustojen tiedostoja ei voi lukea |
| PHP-prosessit | oma PHP-FPM-pooli omalla socketilla | ei yhteistä workeria, omat asetukset |
| PHP-asetukset | open_basedir, oma tmp- ja istuntokansio |
lisäeste, ei yhteisiä tilapäistiedostoja |
| Tiedot | oma tietokantakäyttäjä, jolla on oikeudet vain omaan tietokantaan | ei pääsyä vieraisiin asiakastietoihin |
Suuressa suojaustarpeessa, esimerkiksi verkkokaupoissa, joissa on maksujenkäsittely, tai portaaleissa, joissa on terveystietoja, mukaan tulevat kontit tai erilliset palvelimet.
open_basedir PHP-FPM-poolissa
Seuraava esimerkki näyttää poolin verkkosivustolle, jolla on oma käyttäjä seite1. Polut ja versionumeron muokkaatte omaan järjestelmäänne sopiviksi.
; /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
Viimeinen rivi asettaa samalla php.netin PHP2Shell-väliaikaisen toimen. Se toimii vain poolista, php.ini-tiedostosta tai palvelimen asetuksista, ei ini_set()-komennolla eikä .user.ini-tiedostolla. Kansiot luotte sopivin oikeuksin:
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
Millaiset oikeudet varsinaisessa verkkohakemistossa pitäisi olla, kuvataan oppaassa Verkkopalvelimen tiedosto-oikeudet.
Verkkopalvelimen liittäminen ja tuloksen tarkistus
Nginxissä jokainen verkkosivusto osoittaa omaan socketiinsa. Näin yksikään pyyntö ei päädy toisen sivuston pooliin.
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;
}
}
Lisätietoa tästä yhdistelmästä löydätte sivulta Nginx ja PHP-FPM turvalliseksi. Uudelleenkäynnistyksen jälkeen tarkistatte, toimivatko poolit todella erillään:
sudo systemctl restart php8.3-fpm
# Jokaisen poolin pitäisi toimia omalla käyttäjällään
ps -eo user,pid,cmd | grep "[p]hp-fpm: pool"
Testatkaa sen jälkeen kaikki verkkosivuston toiminnot: lataukset, kuvankäsittely, välimuistit, ajastetut tehtävät. Virhelokin ilmoitukset, joissa mainitaan open_basedir restriction, osoittavat, mikä polku vielä puuttuu. Lisätkää vain polkuja, joita sovellus todella tarvitsee, älkää koskaan koko tiedostojärjestelmää.
Toimistojen, joilla on paljon asiakassivustoja, kannattaa ylläpitää pool-asetuksia mallipohjana. Näin jokainen uusi verkkosivusto saa erotuksen automaattisesti sen sijaan, että se jouduttaisiin lisäämään jälkikäteen. Taustatietoa tähän tapaukseen löytyy sivulta Tietoturvakumppani toimistoille.
Tyypilliset sudenkuopat siirtymässä
Siirtyminen erillisiin pooleihin on suunniteltavissa, mutta siinä on muutama toistuva ansa. Yleisin koskee yhteiskäyttöisiä kirjastoja: jos esimerkiksi viitekehys tai fonttikansio sijaitsee verkkosivuston hakemiston ulkopuolella, tämä polku on lisättävä lukuoikeudella open_basedir-asetukseen. Parempi on tuoda tällaiset riippuvuudet itse projektiin.
Toinen ansa ovat istunnot. Jos tallennuspaikka vaihtuu, kaikki kävijät kirjautuvat kerran ulos ja käynnissä olevat ostoskorit menetetään. Suunnitelkaa siirtymä siksi rauhalliseen aikaikkunaan ja ilmoittakaa siitä sisäisesti.
Kolmanneksi ajastetut tehtävät toimivat usein komentorivin kautta ja siis eri käyttäjänä kuin verkkosivusto. Tarkistakaa cron-tehtävät ja jonot ja ajattakaa ne uuden sivustokäyttäjän alla, muuten syntyy väärän omistajan tiedostoja, joita PHP ei myöhemmin saa muuttaa.
Lopuksi jokaisella poolilla pitäisi olla omat prosessi- ja muistirajat. Näin verkkosivusto, joka joutuu kuormaan tai hyökkäyksen kohteeksi, ei voi kaataa muita mukanaan.
Miten PHP SOS auttaa konkreettisesti?
- Selvitämme, mitkä verkkosivustot jakavat nykyisin käyttäjät, prosessit ja tietokannat.
- Perustamme kullekin verkkosivustolle omat käyttäjät, poolit, tmp-kansiot ja tietokantatunnukset muuttamatta sovelluksia.
- Testaamme jokaisen verkkosivuston siirtymän jälkeen ja korjaamme puuttuvat polut kohdennetusti.
- Dokumentoimme erotuksen mallipohjaksi tuleville projekteille.
Miten tämä sopii kokonaisuuteen, kuvaa palvelu PHP-verkkopalvelimen koventaminen.
Usein kysytyt kysymykset
Riittääkö open_basedir verkkosivustojen erottamiseen toisistaan?
Ei. open_basedir on PHP:n sisäinen lisäeste, ei kova erotus. Eristys on luotettava vasta, kun jokaisella sivustolla on oma järjestelmäkäyttäjä ja pooli, ja suuremmassa suojaustarpeessa kontit tai erilliset palvelimet.
Miksi lataukset eivät enää toimi open_basedir-asetuksen jälkeen?
Yleensä tilapäisten tiedostojen kansio puuttuu luettelosta. Luokaa jokaiselle sivustolle oma tmp-kansio, lisätkää se open_basedir-asetukseen ja asettakaa upload_tmp_dir ja sys_temp_dir osoittamaan tähän kansioon.
Voiko sovellus kumota open_basedir-asetuksen itse?
Sovellus voi ajon aikana vain kiristää asetusta, ei höllentää sitä. Asettakaa open_basedir silti php_admin_value-asetuksella poolissa, jotta arvo on yksiselitteisesti lukittu.
Onko eristys mahdollista myös jaetussa hostingissa?
Se on hosting-yrityksen vastuulla. Monet tarjoukset erottavat asiakkaat omilla käyttäjillä ja PHP-prosesseilla. Kysykää, miten erotus on toteutettu, etenkin jos useita sivustojanne on samassa paketissa.