Lyhyesti: PHP-istuntojen tietoturvassa on neljä ratkaisevaa asiaa: turvalliset evästeasetukset, uusi istuntotunnus jokaisen kirjautumisen jälkeen, erilliset ja suojatut tallennuspaikat sekä lyhyet voimassaoloajat. PHP2Shellin vuoksi session.upload_progress.enabled=0 pitäisi lisäksi asettaa palvelinpuolella, jos tälle toiminnolle ei ole syytä.
Miksi istunnot ovat houkutteleva kohde
Istunto muistaa, että käyttäjä on kirjautunut. Teknisesti selain saa tätä varten satunnaisen istuntotunnuksen evästeessä, ja palvelin tallentaa siihen kuuluvat tiedot. Joka tuntee tämän tunnuksen, on sovellukselle sama käyttäjä, ilman salasanaa ja ilman toista tekijää.
Ylläpitäjille tämä tarkoittaa: heikko istuntohallinta voi kumota kirjautumissuojan, vaikka salasanat ja kaksivaiheinen tunnistautuminen olisi toteutettu siististi. Erityisen kriittistä se on ylläpitoalueilla, asiakastileillä ja portaaleissa, joissa on henkilötietoja.
Tyypillisiä riskejä ovat istuntotunnuksen siepkaus (istuntokaappaus), tunnetun tunnuksen syöttäminen ennen kirjautumista (Session Fixation) ja liian pitkään voimassa olevat istunnot jaetuilla koneilla.
Turvalliset perusasetukset php.ini-tiedostossa
Tärkeimmät arvot voi asettaa keskitetysti php.ini-tiedostossa tai verkkosivustokohtaisesti FPM-poolissa. Poolissa käyttäkää php_admin_value- tai php_admin_flag-asetusta, jotta sovellus ei voi ylikirjoittaa arvoja.
; Istuntojen perusvahvistus
session.use_strict_mode = 1
session.use_only_cookies = 1
session.use_trans_sid = 0
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Lax"
session.cookie_lifetime = 0
session.gc_maxlifetime = 1800
session.name = "APPSESSID"
; php.netin PHP2Shell-ohje, käynnistä sen jälkeen kaikki worker-prosessit uudelleen
session.upload_progress.enabled = 0
use_strict_mode huolehtii siitä, ettei PHP ota käyttöön selaimen antamia tuntemattomia tunnuksia. cookie_httponly estää JavaScriptiä lukemasta tunnusta, mikä rajoittaa Cross-Site Scriptingin seurauksia. cookie_secure lähettää evästeen vain HTTPS:n kautta. SameSite=Lax vaikeuttaa sivustojen välisiä pyyntöjä voimassa olevalla istunnolla; pelkille ylläpitoalueille voi sopia Strict.
Voimassaoloaika 1800 sekuntia on esimerkkiarvo. Määrittäkää se suojaustarpeen mukaan: lyhyempi ylläpidolle ja asiakastiedoille, pidempi vain siellä, missä se on toiminnallisesti tarpeen.
Huomatkaa, että gc_maxlifetime määrittää vain, mistä lähtien vanhat istuntotiedot saa poistaa. Joissakin jakeluissa istuntotiedostot siivoaa PHP:n sijaan säännöllinen järjestelmätehtävä. Tarkistakaa silloin, mitä arvoa tämä tehtävä käyttää, jotta asetuksenne todella tehoaa. Älkää myöskään luottako uloskirjautumisessa käyttämättömyyden jälkeen pelkkään siivoustoimintoon, vaan tarkistakaa viimeisen toiminnon ajankohta sovelluksessa itse, kuten seuraavassa esimerkissä näytetään.
Istuntotunnuksen uusiminen kirjautumisen jälkeen
Koodin tärkein sääntö on luoda istuntotunnus uudelleen jokaisen oikeuksien muutoksen yhteydessä, siis kirjautumisessa, siirryttäessä ylläpitotilaan ja uloskirjautumisessa. Seuraava esimerkki näyttää siistin perusmallin password_verify()-funktiolla:
<?php
declare(strict_types=1);
session_start();
function anmelden(PDO $db, string $email, string $passwort): bool
{
$stmt = $db->prepare('SELECT id, passwort_hash FROM benutzer WHERE email = :email');
$stmt->execute(['email' => $email]);
$nutzer = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$nutzer || !password_verify($passwort, $nutzer['passwort_hash'])) {
return false; // sama ilmoitus väärälle osoitteelle ja väärälle salasanalle
}
session_regenerate_id(true); // uusi tunnus, vanha hylätään
$_SESSION['benutzer_id'] = (int) $nutzer['id'];
$_SESSION['letzte_aktion'] = time();
return true;
}
function pruefeLeerlauf(int $sekunden = 900): void
{
if (isset($_SESSION['letzte_aktion']) && time() - $_SESSION['letzte_aktion'] > $sekunden) {
abmelden();
return;
}
$_SESSION['letzte_aktion'] = time();
}
function abmelden(): void
{
$_SESSION = [];
$p = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => time() - 3600,
'path' => $p['path'],
'domain' => $p['domain'],
'secure' => $p['secure'],
'httponly' => $p['httponly'],
'samesite' => $p['samesite'],
]);
session_destroy();
}
Suojatkaa lisäksi lomakkeet, jotka muuttavat jotain, CSRF-tunnisteella. Se luodaan funktiolla bin2hex(random_bytes(32)), tallennetaan istuntoon ja verrataan lähetettäessä funktiolla hash_equals().
Tallennuspaikat ja asiakaserotus
Oletuksena PHP tallentaa istunnot tiedostoina yhteiseen hakemistoon. Palvelimella, jolla on useita verkkosivustoja, se voi tarkoittaa, että vaarantunut verkkosivusto voi lukea toisen sivuston istuntoja. Siksi jokaiselle verkkosivustolle kuuluu oma pooli, oma käyttäjä ja oma istuntopolku.
; kunkin verkkosivuston FPM-poolissa
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Hakemiston pitäisi kuulua pool-käyttäjälle ja sillä pitäisi olla oikeudet 0700. Miten erotatte verkkosivustot toisistaan lisäksi, kuvataan oppaassa open_basedir ja sivustokohtainen eristys. Jos istunnot tallennetaan Redisiin tai tietokantaan, pätevät samat periaatteet: oma pääsy kutakin sovellusta kohden, ei avointa verkkoyhteyttä.
PHP-istuntojen tietoturva ja PHP2Shell
PHP2Shellin haavoittuvuudet CVE-2026-109618 ja CVE-2026-109619 ovat PHP:ssä itsessään. Yhtenä väliaikaisena toimena php.net mainitsee asettaa session.upload_progress.enabled=0 php.ini-tiedostossa, FPM-poolissa tai palvelimen asetuksissa ja käynnistää sen jälkeen kaikki worker-prosessit uudelleen. Nimenomaan ei ini_set()-komennolla eikä .user.ini-tiedostolla.
Tiedote CVE-2026-115560:stä tuli 23.10.2026. Mahdollinen koodin suoritus on siinä mahdollista vain epätavallisella istuntojen serialisoinnin, ennen session_start()-kutsua ini_set()-komennolla tehdyn vaihdon ja aktiivisen Upload Progressin yhdistelmällä. Keino on sama asetus seuraavaan tavanomaiseen virhekorjausjulkaisuun asti.
Tämä asetus on väliaikainen toimi eikä täydellinen suoja. Ratkaisevaa on yhä päivitys korjattuun PHP-versioon ja sitä seuraava kaikkien PHP-worker-prosessien uudelleenkäynnistys.
Tarkistakaa muutoksen jälkeen, mikä arvo todella on voimassa. Komentorivi näyttää usein eri asetukset kuin verkkopalvelin; luotettava keino on suojattu, sen jälkeen poistettu tarkistussivu tai itse pool-asetukset.
Tyypilliset virheet istunnoissa PHP-sovelluksissa
Monet heikkoudet eivät synny puuttuvista asetuksista vaan siitä, miten sovellukset käyttävät istuntoja. Nämä mallit kannattaa selvittää kohdennetusti omasta koodistanne ja palveluntarjoajilta:
- Liikaa tietoa istunnossa: Salasanat, täydelliset maksutiedot tai suuret tietueet eivät kuulu istuntoon. Tallentakaa vain se, mikä on tarpeen tunnistamiseen, esimerkiksi käyttäjätunnus ja roolit.
- Oikeudet vain istunnosta: Se, onko joku ylläpitäjä, pitäisi kriittisissä toimissa tarkistaa tietokannasta. Muuten poistetut oikeudet pysyvät voimassa istunnon päättymiseen asti.
- Istuntotunnus osoitteessa: URL-osoitteiden tunnukset päätyvät lokeihin, selainhistoriaan ja Referer-tietoihin.
use_only_cookiesjause_trans_sid = 0estävät tämän. - Istunnolla ei ole loppua: Jos käyttämättömyyden aikakatkaisua ei ole, ylläpitäjä pysyy jaetulla koneella kirjautuneena rajattomasti.
- HTTPS tunnistetaan väärin välityspalvelimen takana: Jos salaus päättyy kuormantasaajaan, sovelluksen on tiedettävä se. Muuten se voi asettaa evästeet ilman
Secure-lippua.
Tietoturvapoikkeaman jälkeen kuuluu lisäksi hylätä kaikki olemassa olevat istunnot. Helpoin tapa on poistaa istuntohakemiston sisältö tai vaihtaa istunnon nimi. Kaikkien käyttäjien on silloin kirjauduttava uudelleen, ja varastetut istunnot ovat arvottomia.
Miten PHP SOS auttaa konkreettisesti?
- Tarkistamme istunta-asetukset, evästeiden liput ja tallennuspaikat palvelimen kaikilla verkkosivustoilla ja dokumentoimme todella voimassa olevat arvot.
- Otamme PHP2Shellin väliaikaiset toimet käyttöön pooleittain, käynnistämme worker-prosessit hallitusti uudelleen ja osoitamme tuloksen.
- Erotamme verkkosivustot omilla pooleilla ja istuntopoluilla osana palvelimen koventamista.
- Omille kehitystöille tarkistamme kirjautumisen, uloskirjautumisen ja istunnon vaihdon koodissa ja suosittelemme konkreettisia muutoksia.
Usein kysytyt kysymykset
Mikä on istuntokaappaus?
Istuntokaappauksessa hyökkääjä ottaa kirjautuneen käyttäjän istunnon haltuunsa saamalla tämän istuntotunnuksen. Tunnuksella hän on sovellukselle kyseinen käyttäjä tuntematta salasanaa. Turvalliset evästeiden liput ja lyhyet voimassaoloajat vaikeuttavat tätä selvästi.
Miksi istuntotunnus on luotava uudelleen kirjautumisen jälkeen?
Jos tunnus pysyy samana ennen kirjautumista ja sen jälkeen, aiemmin tunnettu tai syötetty tunnus voisi olla voimassa kirjautumisen jälkeenkin. session_regenerate_id(true) luo uuden tunnuksen ja hylkää vanhan.
Voinko poistaa session.upload_progress.enabled-asetuksen käytöstä ini_set()-komennolla?
Ei, se ei php.netin mukaan riitä. Asetus kuuluu php.ini-tiedostoon, FPM-pooliin tai palvelimen asetuksiin, ei ini_set()-komentoon eikä .user.ini-tiedostoon. Sen jälkeen kaikki PHP-worker-prosessit on käynnistettävä uudelleen.
Minne istuntotiedostot pitäisi tallentaa?
Web-juuren ulkopuoliseen hakemistoon, jota saa lukea vain kyseisen PHP-poolin käyttäjä. Palvelimilla, joilla on useita verkkosivustoja, jokaisella sivustolla pitäisi olla oma istuntohakemisto.