Dabartinė padėtis: PHP2Shell. Du kritiniai PHP pažeidžiamumai (CVSS 9,2) leidžia vykdyti kenkėjišką kodą be prisijungimo. Pataisos nuo 2026-10-21.Ką dabar daryti →

Klaidos ir išimtys PHP

PHP žino klasikinius klaidų pranešimus ir išimtis (exceptions). Šiame puslapyje paaiškinta Throwable hierarchija, tvarkingas klaidų apdorojimas, saugūs nustatymai produkcijai ir PHP2Shell priemonė dėl fatal_error_backtraces.

Būsena 4 min. skaitymoPHP žinios

Trumpai: PHP apie problemas praneša dviem būdais: klasikinėmis klaidomis, tokiomis kaip įspėjimai ir pastabos, kurias valdo error_reporting, ir išimtimis (exceptions), kurias galima sugauti su try ir catch. Nuo PHP 7 daug vidinių klaidų taip pat išmeta Error klasės objektą; kartu su Exception jis įgyvendina sąsają Throwable. Produkcijoje galioja taisyklė: klaidas protokoluoti, niekada nerodyti naršyklėje. PHP 8.5 dėl PHP2Shell prie pereinamųjų priemonių papildomai priklauso fatal_error_backtraces=0.

Du pasauliai: klasikinės klaidos ir išimtys

PHP atsirado laikais, kai klaidos daugiausia būdavo išvedamos kaip pranešimas: pastabos (E_NOTICE), įspėjimai (E_WARNING), pasenusios funkcijos (E_DEPRECATED) ir mirtinos klaidos (E_ERROR). Daug šių pranešimų leidžia skriptui veikti toliau, dažnai su neteisingais ar tuščiais duomenimis.

Išimtys veikia kitaip. Jos nutraukia įprastą eigą ir peršoka į artimiausią tinkamą catch bloką. Jei tokio nerandama, skriptas baigiasi. Sprendimų priėmėjams skirtumas svarbus: tylus įspėjimas gali lemti, kad užsakymas išsaugomas neišsamiai, ir niekas to nepastebi. Išimtis problemą padaro matomą ir apdorojamą.

Nepakankamai vertinama klasikinių pranešimų nauda yra tipo E_DEPRECATED pastabos. Jos praneša, kad kalbos priemonė būsimoje PHP versijoje bus pašalinta. Kas jas renka žurnale ir reguliariai vertina, prieš atnaujindamas žino, kurias vietas reikia pritaikyti, ir ištikus skubiai saugumo spragai neturi ilgai testuoti.

Throwable hierarchija

Viskas, ką galima išmesti su throw ir sugauti su catch, įgyvendina Throwable. Po juo yra dvi šakos:

Klasė Paskirtis Poklasių pavyzdžiai
Exception nuspėjamos programos ir plėtinių klaidos ErrorException, PDOException, savos klasės
Error vidinės PHP klaidos TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError

Savos klaidų klasės paveldi iš Exception, o ne iš Error. Nuo PHP 8.0 dauguma vidinių funkcijų esant netinkamiems parametrams išmeta TypeError ar ValueError, o ne tik išveda įspėjimą. strlen([]) yra vienas tokių atvejų.

try, catch ir finally praktikoje

<?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) {
        // nuo PHP 8.0 kintamasis catch dalyje neprivalomas
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}

Kelias klases viename catch galima sujungti su |. Blokas finally vykdomas visada, nesvarbu, ar įvyko klaida. Nuo PHP 8.0 throw taip pat yra išraiška ir gali stovėti, pavyzdžiui, po ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.

Globalios tvarkyklės: set_error_handler ir set_exception_handler

Dvi funkcijos sugauna tai, kas kitaip praslystų. set_exception_handler() užregistruoja funkciją nesugautiems Throwable. Pasak vadovo, ją iškvietus skriptas baigiasi; todėl ji tinka protokolavimui ir neutraliam klaidos puslapiui, o ne tolesniam darbui.

Praktiškai pasiteisino tvarkyklė, kuri visą klaidą su laiku ir atsitiktiniu identifikatoriumi įrašo į žurnalą, o lankytojui rodo tik neutralų puslapį su tuo identifikatoriumi. Pagalbos tarnyba tuomet gali rasti atvejį žurnale, neatskleisdama vidinių detalių išorei.

set_error_handler() gaudo klasikines klaidas. Vadovas rodo, kaip jas paversti ErrorException, kad įspėjimai būtų apdorojami kaip išimtys:

<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // error_reporting išskirta
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // pastabų apie pasenusį kodą neeskaluoti
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});

Mirtinų klaidų, tokių kaip atminties perpildymas, tokiu būdu sugauti negalima. Kas viduje vyksta perpildymo metu, paaiškina puslapis memory_limit ir Out-of-Memory.

Klaidų rodymo ir protokolavimo konfigūravimas

Svarbiausi nustatymai su numatytosiomis reikšmėmis pagal vadovą:

Nustatymas Numatytasis Rekomendacija produkcijai
error_reporting E_ALL nuo PHP 8.0.0 E_ALL
display_errors 1 Off
display_startup_errors 1 nuo PHP 8.0.0 Off
log_errors 0 On
error_log tuščias (žiniatinklio serverio arba SAPI žurnalas) atskiras failas už žiniatinklio šakninio katalogo ribų
fatal_error_backtraces 1, nauja PHP 8.5.0 laikinai 0 (PHP2Shell)

Nuo PHP 8.2.0 error_log_mode nustato žurnalo failo teises, pagal numatymą 0644. Jei žurnaluose yra asmens duomenų ar kelių, prasminga griežtesnė reikšmė, kad kiti serverio naudotojai jų neperskaitytų. Su ignore_repeated_errors galima sujungti identiškus pranešimus iš to paties failo ir eilutės, kad klaidų ciklas neužtvindytų žurnalo.

Kartu pateikiamas php.ini-production jau nustato display_errors į Off. Pasak vadovo, ini_set('display_errors', ...) vykdymo metu mirtinoms klaidoms neveikia, o sintaksės klaidos atsiranda dar prieš pradedant vykdyti bet kokį ini_set(). Todėl šios reikšmės turi būti serverio konfigūracijoje. Pagrindimą rasite straipsnyje Klaidų rodymo išjungimas produkcijoje; kaip žurnalus vesti be duomenų nutekėjimo, aprašyta straipsnyje Saugus protokolavimas.

Klaidų apdorojimas ir PHP2Shell

PHP 8.5 php.net kaip vieną iš pereinamųjų priemonių nuo PHP2Shell nurodo nustatyti fatal_error_backtraces=0 ir po to paleisti PHP iš naujo. Kitos priemonės liečia memory_limit, post_max_size ir session.upload_progress.enabled. Nė viena jų neatstoja atnaujinimo iki PHP 8.5.12, 8.4.27, 8.3.36 ar 8.2.35.

; tik PHP 8.5, php.ini arba FPM baseine, po to visus darbinius procesus (worker) paleisti iš naujo
fatal_error_backtraces = 0

Po PHP2Shell klaidų žurnalai taip pat yra svarbus įrodymas. Jei nuo 2026-10-20 dažnėja atminties klaidos ar nutrūkę darbiniai procesai, tai gali rodyti atakos bandymus, bet gali turėti ir nekenksmingų priežasčių. Išsaugokite žurnalus ir vertinkite juos kartu su prieigos žurnalais, kaip aprašyta vadove Žiniatinklio serverio žurnalų vertinimas po atakos.

Kaip PHP SOS padeda konkrečiai?

  • Tikriname veikiančius klaidų rodymo ir protokolavimo nustatymus kiekvienai svetainei ir nustatome juos tinkamai produkcijai.
  • Pereinamąsias PHP2Shell priemones nustatome pagal PHP versiją ir paleidžiame iš naujo visus darbinius procesus (worker).
  • Išsaugome klaidų ir prieigos žurnalus nuo 2026-10-20 ir vertiname juos dėl neįprastumų.
  • Pagal saugumo stebėsenos paslaugą pranešame apie neįprastą klaidų dažnėjimą, užuot leidę jam paskęsti žurnale.

Dažni klausimai

Kuo skiriasi Error ir Exception?

Abi įgyvendina sąsają Throwable. Exception skirta klaidoms, kurių programa tikisi ir jas apdoroja, pavyzdžiui, nepavykusiam prisijungimui prie duomenų bazės. Error žymi vidines PHP klaidas, tokias kaip TypeError ar dalyba iš nulio.

Ar produkcijoje display_errors turi būti įjungtas?

Ne. Klaidų pranešimai naršyklėje atskleidžia kelius, užklausas, o kartais ir prieigos duomenis. Produkcijoje display_errors turi būti Off, o log_errors On, kad klaidos patektų į žurnalą, o ne pas lankytoją.

Ar atminties perpildymą galima sugauti su try/catch?

Ne. Jei skriptas viršija memory_limit, PHP jį nutraukia su mirtina klaida, kurios negalima sugauti kaip išimties. Ji matoma tik klaidų žurnale, jei log_errors aktyvus.

Kas yra fatal_error_backtraces?

Nustatymas, egzistuojantis nuo PHP 8.5.0, kuris prideda atgalinį sekimą (backtrace) prie mirtinų klaidų. Pagal numatymą jis aktyvus. Kaip pereinamąją priemonę nuo PHP2Shell php.net PHP 8.5 nurodo nustatyti jį į 0 ir po to paleisti PHP iš naujo.

Šaltiniai

Skubus kontaktas

Kiekviena valanda svarbi. Parašykite mums dabar.

Trumpai aprašykite sistemą, PHP versiją ir pastebėjimus. Mes atsakysime, išsiaiškinsime padėtį ir dirbsime nuotoliu, kol incidentas bus užbaigtas ir dokumentuotas.

Skubi pagalba