Ajankohtainen tilanne: PHP2Shell. Kaksi kriittistä PHP-haavoittuvuutta (CVSS 9.2) sallii haittakoodin suorittamisen ilman kirjautumista. Korjaukset 21.10.2026 alkaen.Mitä nyt on tehtävä →

Virheet ja poikkeukset PHP:ssä

PHP tuntee perinteiset virheilmoitukset ja poikkeukset (exception). Tällä sivulla selitetään Throwable-hierarkia, siisti virheenkäsittely, tuotannon turvalliset asetukset ja PHP2Shell-toimenpide fatal_error_backtraces.

Tilanne Lukuaika 4 minPHP-tietoa

Lyhyesti: PHP ilmoittaa ongelmista kahdella tavalla: perinteisinä virheinä, kuten varoituksina ja huomautuksina, joita ohjataan asetuksella error_reporting, sekä poikkeuksina, jotka voi pyydystää rakenteella try ja catch. Versiosta PHP 7 lähtien myös monet sisäiset virheet heittävät luokan Error olion; yhdessä luokan Exception kanssa se toteuttaa rajapinnan Throwable. Tuotannossa pätee: virheet kirjataan lokiin, niitä ei koskaan näytetä selaimessa. PHP 8.5:ssä PHP2Shellin vuoksi myös fatal_error_backtraces=0 kuuluu väliaikaisiin toimenpiteisiin.

Kaksi maailmaa: perinteiset virheet ja poikkeukset

PHP on peräisin ajalta, jolloin virheet tulostettiin ennen kaikkea ilmoituksina: huomautukset (E_NOTICE), varoitukset (E_WARNING), vanhentuneet funktiot (E_DEPRECATED) ja vakavat virheet (E_ERROR). Monet näistä ilmoituksista antavat komentosarjan jatkaa, usein väärillä tai tyhjillä tiedoilla.

Poikkeukset toimivat toisin. Ne keskeyttävät normaalin kulun ja hyppäävät seuraavaan sopivaan catch-lohkoon. Jos sellaista ei löydy, komentosarja päättyy. Päättäjälle ero on tärkeä: hiljainen varoitus voi johtaa siihen, että tilaus tallentuu puutteellisena kenenkään huomaamatta. Poikkeus tekee ongelmasta näkyvän ja käsiteltävän.

Aliarvioitu hyöty perinteisissä ilmoituksissa ovat tyypin E_DEPRECATED huomautukset. Ne ennakoivat, että jokin kielen ominaisuus poistuu tulevasta PHP-versiosta. Kun ne kerätään lokiin ja käydään säännöllisesti läpi, tiedetään ennen päivitystä, mitkä kohdat on mukautettava, eikä kiireellisen tietoturva-aukon kohdalla tarvitse ensin testata pitkään.

Throwable-hierarkia

Kaikki, minkä voi heittää rakenteella throw ja pyydystää rakenteella catch, toteuttaa rajapinnan Throwable. Sen alla on kaksi haaraa:

Luokka Tarkoitus Esimerkkejä aliluokista
Exception sovelluksen ja laajennusten odotettavissa olevat virheet ErrorException, PDOException, omat luokat
Error PHP:n sisäiset virheet TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError

Omat virheluokat periytetään luokasta Exception, ei luokasta Error. Versiosta PHP 8.0 lähtien useimmat sisäiset funktiot heittävät virheellisistä parametreista TypeError- tai ValueError-virheen pelkän varoituksen tulostamisen sijaan. strlen([]) on tällainen tapaus.

try, catch ja finally käytännössä

<?php
declare(strict_types=1);

final class ZahlungFehlgeschlagen extends RuntimeException {}

function bezahlen(Bestellung $b, Zahlungsdienst $dienst, LoggerInterface $log): bool
{
    try {
        $dienst->belasten($b->betrag(), $b->referenz());
        return true;
    } catch (ZahlungFehlgeschlagen $e) {
        $log->warning('Zahlung abgelehnt', ['bestellung' => $b->id(), 'exception' => $e]);
        return false;
    } catch (TimeoutException | VerbindungsFehler) {
        // versiosta PHP 8.0 lähtien muuttuja catchissa on valinnainen
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}

Useita luokkia voi koota yhteen catch-lohkoon merkillä |. finally-lohko suoritetaan aina, tapahtui virhe tai ei. Versiosta PHP 8.0 lähtien throw on lisäksi lauseke ja voi olla esimerkiksi ??-operaattorin perässä: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.

Globaalit käsittelijät: set_error_handler ja set_exception_handler

Kaksi funktiota pyydystää sen, mikä muuten pääsisi läpi. set_exception_handler() rekisteröi funktion käsittelemättömille Throwable-olioille. Käsikirjan mukaan komentosarja päättyy sen kutsun jälkeen; se sopii siis kirjaamiseen ja neutraalille virhesivulle, ei toiminnan jatkamiseen.

Käytännössä hyväksi on osoittautunut käsittelijä, joka kirjoittaa koko virheen lokiin aikaleiman ja satunnaisen tunnisteen kanssa ja näyttää kävijälle vain neutraalin sivun tällä tunnisteella. Tuki löytää tapauksen silloin lokista ilman, että sisäiset yksityiskohdat vuotavat ulos.

set_error_handler() pyydystää perinteiset virheet. Käsikirja näyttää, miten ne muunnetaan ErrorException-poikkeukseksi, jotta varoituksia käsitellään kuin poikkeuksia:

<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // error_reporting on rajannut pois
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // älä eskaloi vanhentuneisuushuomautuksia
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});

Vakavia virheitä, kuten muistin ylitystä, ei voi tällä tavalla pyydystää. Mitä ylityksessä tapahtuu sisäisesti, selitetään sivulla memory_limit ja Out-of-Memory.

Virheiden näytön ja kirjaamisen asetukset

Tärkeimmät asetukset oletusarvoineen käsikirjan mukaan:

Asetus Oletus Suositus tuotantoon
error_reporting E_ALL versiosta PHP 8.0.0 lähtien E_ALL
display_errors 1 Off
display_startup_errors 1 versiosta PHP 8.0.0 lähtien Off
log_errors 0 On
error_log tyhjä (verkkopalvelimen tai SAPI:n loki) oma tiedosto verkkojuuren ulkopuolella
fatal_error_backtraces 1, uusi versiossa PHP 8.5.0 väliaikaisesti 0 (PHP2Shell)

Versiosta PHP 8.2.0 lähtien error_log_mode määrittää lokitiedoston käyttöoikeudet, oletuksena 0644. Jos lokeissa on henkilötietoja tai polkuja, tiukempi arvo on järkevä, jotta muut palvelimen käyttäjät eivät voi lukea niitä. Asetuksella ignore_repeated_errors samasta tiedostosta ja riviltä tulevat identtiset ilmoitukset voi koota yhteen, jotta virhesilmukka ei täytä lokia.

Mukana tuleva php.ini-production asettaa display_errors-arvon jo valmiiksi tilaan Off. Käsikirjan mukaan ini_set('display_errors', ...) ei vaikuta ajon aikana vakaviin virheisiin, ja syntaksivirheet ilmenevät ennen kuin yksikään ini_set() ehtii edes suorittua. Nämä arvot kuuluvat siksi palvelimen konfiguraatioon. Taustoja on artikkelissa Virheiden näytön poistaminen tuotannosta; miten lokeja pidetään ilman tietovuotoa, kerrotaan artikkelissa Turvallinen lokitus.

Virheenkäsittely ja PHP2Shell

PHP 8.5:lle php.net mainitsee yhdeksi PHP2Shelliä vastaan tarkoitetuksi väliaikaiseksi toimenpiteeksi asetuksen fatal_error_backtraces=0 ja PHP:n uudelleenkäynnistyksen sen jälkeen. Muut toimenpiteet koskevat asetuksia memory_limit, post_max_size ja session.upload_progress.enabled. Mikään niistä ei korvaa päivitystä versioon PHP 8.5.12, 8.4.27, 8.3.36 tai 8.2.35.

; vain PHP 8.5, php.ini-tiedostossa tai FPM-poolissa, sen jälkeen kaikkien workerien uudelleenkäynnistys
fatal_error_backtraces = 0

Virhelokit ovat PHP2Shellin jälkeen myös tärkeä todiste. Jos muistivirheitä tai keskeytyneitä worker-prosesseja kertyy 20.10.2026 alkaen, se voi viitata hyökkäysyrityksiin, mutta syynä voi olla myös jokin vaaraton asia. Säilyttäkää lokit ja käykää ne läpi yhdessä käyttölokien kanssa oppaan Verkkopalvelimen lokien analysointi hyökkäyksen jälkeen mukaisesti.

Miten PHP SOS auttaa konkreettisesti?

  • Tarkistamme virheiden näytön ja kirjaamisen voimassa olevat asetukset sivustoittain ja asetamme ne tuotantokelpoisiksi.
  • Otamme PHP2Shellin väliaikaiset toimenpiteet käyttöön PHP-versiolle sopivina ja käynnistämme kaikki worker-prosessit uudelleen.
  • Turvaamme virhe- ja käyttölokit 20.10.2026 alkaen ja käymme ne läpi poikkeamien varalta.
  • Tietoturvaseurannan puitteissa ilmoitamme epätavallisista virhekasaumista sen sijaan, että antaisimme niiden hukkua lokiin.

Usein kysytyt kysymykset

Mikä on ero Errorin ja Exceptionin välillä?

Molemmat toteuttavat rajapinnan Throwable. Exception on tarkoitettu virheisiin, joita sovellus odottaa ja käsittelee, kuten epäonnistunut tietokantayhteys. Error edustaa PHP:n sisäisiä virheitä, kuten TypeError tai nollalla jakaminen.

Pitäisikö display_errors olla tuotannossa päällä?

Ei pitäisi. Selaimessa näkyvät virheilmoitukset paljastavat polkuja, kyselyjä ja joskus tunnuksia. Tuotannossa display_errors kuuluu asettaa arvoon Off ja log_errors arvoon On, jolloin virheet päätyvät lokiin eivätkä kävijälle.

Voiko muistin ylitystä pyydystää try/catchilla?

Ei voi. Kun komentosarja ylittää memory_limit-rajan, PHP lopettaa sen vakavaan virheeseen, jota ei voi pyydystää poikkeuksena. Se näkyy vain virhelokissa, jos log_errors on käytössä.

Mikä on fatal_error_backtraces?

Asetus, joka on ollut olemassa versiosta PHP 8.5.0 lähtien ja liittää vakaviin virheisiin backtracen. Se on oletuksena päällä. Väliaikaisena toimenpiteenä PHP2Shelliä vastaan php.net neuvoo PHP 8.5:ssä asettamaan sen arvoon 0 ja käynnistämään PHP sen jälkeen uudelleen.

Lähteet

Pikayhteys

Jokainen tunti on tärkeä. Kirjoittakaa meille nyt.

Kuvatkaa lyhyesti järjestelmä, PHP-versio ja havainnot. Otamme yhteyttä, selvitämme tilanteen ja työskentelemme etänä, kunnes tapaus on käsitelty loppuun ja dokumentoitu.

Pikaapu