Τρέχουσα κατάσταση: PHP2Shell. Δύο κρίσιμες ευπάθειες της PHP (CVSS 9.2) επιτρέπουν κακόβουλο κώδικα χωρίς σύνδεση. Patches από τις 21/10/2026.Τι πρέπει να γίνει τώρα →

Ο κύκλος ζωής του αιτήματος: εκκίνηση, εκτέλεση, τερματισμός

Πώς η PHP ξεκινά διεργασίες, επεξεργάζεται αιτήματα και καθαρίζει ενδιάμεσα. Με τις φάσεις από MINIT έως MSHUTDOWN και τη σχέση με την CVE-2026-109619.

Ημερομηνία 6 λεπτά ανάγνωσηςΓνώση PHP

Εν συντομία: Ο κύκλος ζωής του αιτήματος PHP έχει δύο επίπεδα. Μία φορά ανά διεργασία η PHP φορτώνει τις επεκτάσεις της (φάση MINIT) και στο τέλος τις αποσυναρμολογεί ξανά (MSHUTDOWN). Για κάθε μεμονωμένο αίτημα υπάρχει μια δική του φάση εκκίνησης και καθαρισμού (RINIT και RSHUTDOWN). Στη λειτουργία ιστού ένας worker επεξεργάζεται πολλά αιτήματα το ένα μετά το άλλο, γι' αυτό η PHP πρέπει ανάμεσα σε δύο αιτήματα να επαναφέρει αξιόπιστα όλη την κατάσταση του αιτήματος. Ακριβώς σε αυτό το όριο βρισκόταν η CVE-2026-109619, η δεύτερη ευπάθεια PHP2Shell.

Δύο επίπεδα: η διεργασία και το αίτημα

Για όσους δεν είναι τεχνικοί βοηθά μια εικόνα: ένας worker PHP είναι σαν ένα γκισέ σε δημόσια υπηρεσία. Το γκισέ στήνεται μία φορά το πρωί, με έντυπα, σφραγίδες και υπολογιστή. Στη συνέχεια έρχονται διαδοχικά πολλοί επισκέπτες. Μετά από κάθε επισκέπτη το τραπέζι πρέπει να είναι άδειο, ώστε ο επόμενος να μη δει ξένα έγγραφα.

Η PHP λειτουργεί με τον ίδιο τρόπο. Κατά την εκκίνηση μιας διεργασίας φορτώνει επεκτάσεις και ρυθμίσεις που είναι ίδιες για όλα τα αιτήματα. Κάθε αίτημα λαμβάνει στη συνέχεια δική του μνήμη, δικές του μεταβλητές και δικά του δεδομένα αιτήματος, όπως τα $_GET, $_POST ή $_FILES. Στο τέλος του αιτήματος η PHP ελευθερώνει αυτή τη μνήμη. Το PHP Internals Book το συνοψίζει σύντομα: η PHP ξεχνά τη μνήμη του αιτήματος στο τέλος κάθε αιτήματος.

Οι φάσεις του κύκλου ζωής του αιτήματος PHP με μια ματιά

Τα ονόματα προέρχονται από το εσωτερικό της PHP· κάθε επέκταση μπορεί να συνδεθεί σε αυτές τις φάσεις.

Φάση Πότε εκτελείται Πόσο συχνά Τυπική εργασία
MINIT (Module Init) κατά την εκκίνηση της διεργασίας μία φορά ανά διεργασία καταχώριση επεκτάσεων, δημιουργία εγγραφών ini, μόνιμα δεδομένα
GINIT κατά τη δημιουργία των globals ανά διεργασία, στα threads ανά thread προετοιμασία καθολικών μεταβλητών μιας επέκτασης
RINIT (Request Init) στην αρχή κάθε αιτήματος ανά αίτημα προετοιμασία μνήμης και κατάστασης του αιτήματος
Εκτέλεση ενδιάμεσα ανά αίτημα εκτελείται ο κώδικας PHP σας
RSHUTDOWN στο τέλος κάθε αιτήματος ανά αίτημα ελευθέρωση πόρων του αιτήματος
PRSHUTDOWN μετά το RSHUTDOWN ανά αίτημα, σπάνια χρησιμοποιείται τελευταίες εργασίες καθαρισμού
MSHUTDOWN κατά τον τερματισμό της διεργασίας μία φορά ανά διεργασία ελευθέρωση μόνιμων δεδομένων

Μια σημαντική πρόταση από το PHP Internals Book για τους προγραμματιστές επεκτάσεων: τα globals δεν αδειάζουν αυτόματα μετά από κάθε αίτημα. Ό,τι πρέπει να είναι φρέσκο ανά αίτημα, ανήκει στο RINIT να επαναφέρεται. Αυτός ο κανόνας δείχνει γιατί το όριο μεταξύ δύο αιτημάτων είναι ένα κρίσιμο για την ασφάλεια σημείο.

Στο τέλος ενός αιτήματος η σειρά είναι επίσης καθορισμένη: πρώτα εκτελούνται οι συναρτήσεις shutdown που καταχωρίσατε με την register_shutdown_function(), έπειτα οι destructors και κατόπιν αδειάζει ο ενταμιευτής εξόδου. Μόνο μετά ακολουθεί το RSHUTDOWN.

Γιατί οι workers επαναχρησιμοποιούνται

Στη γραμμή εντολών μια διεργασία επεξεργάζεται ακριβώς ένα αίτημα και τελειώνει. Στη λειτουργία ιστού αυτό θα ήταν υπερβολικά αργό. Το PHP-FPM, ο Apache με mod_php, το LiteSpeed και το FrankenPHP κρατούν γι' αυτό έτοιμους workers που εξυπηρετούν πολλά αιτήματα στη σειρά. Το MINIT εκτελείται τότε μία φορά, τα RINIT και RSHUTDOWN χιλιάδες φορές. Πώς το FPM ελέγχει τους workers του, εξηγεί η σελίδα Το PHP-FPM εξηγημένο.

Στο PHP-FPM το pm.max_requests ορίζει μετά από πόσα αιτήματα επανεκκινεί ένας worker. Η προεπιλογή είναι 0, δηλαδή απεριόριστα. Το εγχειρίδιο αναφέρει τη ρύθμιση ως μέσο κατά των διαρροών μνήμης σε βιβλιοθήκες τρίτων:

; Pool FPM: επανεκκίνηση του worker μετά από 500 αιτήματα (κατά των διαρροών μνήμης)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

Ακόμη πιο μακριά πάει ο τρόπος λειτουργίας worker του FrankenPHP: εκεί ολόκληρη η εφαρμογή εκκινεί μία φορά και παραμένει στη μνήμη. Η κατάσταση της εφαρμογής επιβιώνει τότε και σε επίπεδο PHP. Το παράδειγμα δείχνει τη διαφορά:

<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // στην κλασική λειτουργία FPM, ξανά 0 σε κάθε αίτημα
    return ++$zaehler;     // στον τρόπο λειτουργίας worker η τιμή συνεχίζει να αυξάνεται ανάμεσα στα αιτήματα
}
echo anfrageZaehler();

Στην κλασική λειτουργία η κλήση εμφανίζει πάντα 1, επειδή η PHP απορρίπτει τη μνήμη του αιτήματος. Στον τρόπο λειτουργίας worker η τιμή αυξάνεται όσο ζει ο worker. Η τεκμηρίωση του FrankenPHP συνιστά γι' αυτό η κατάσταση που αφορά το αίτημα να επαναφέρεται συνειδητά ανάμεσα στα αιτήματα.

Πού στοχεύει η CVE-2026-109619

Η CVE-2026-109619 δεν αφορά τον κώδικα της εφαρμογής σας, αλλά την ίδια την PHP. Σύμφωνα με το php.net, εσωτερική κατάσταση που ανήκε στο αίτημα μπορούσε να επιβιώσει της απελευθέρωσης της μνήμης της. Ένα μεταγενέστερο αίτημα στον ίδιο worker μπορούσε να τη χρησιμοποιήσει πριν την επαναφέρει η αρχικοποίηση. Στην εικόνα: στο τραπέζι του γκισέ έμεινε ένα σημείωμα που θα έπρεπε να είχε ήδη απομακρυνθεί. Λεπτομέρειες εκμετάλλευσης δεν δημοσιεύουμε σκόπιμα· την κατάταξη θα βρείτε στη σελίδα CVE-2026-109619.

Τα προσωρινά μέτρα του php.net για αυτή την ευπάθεια στηρίζονται σε επιβεβαιωμένες διαδρομές, δεν αποτελούν πλήρη προστασία:

  1. session.upload_progress.enabled=0 στο php.ini, στο pool του FPM ή στη διαμόρφωση του διακομιστή, και στη συνέχεια επανεκκίνηση όλων των workers· όχι μέσω ini_set() ή .user.ini.
  2. Στην PHP 8.5 επιπλέον fatal_error_backtraces=0 και επανεκκίνηση.
  3. Αφαίρεση άκυρων ή παρωχημένων ρυθμίσεων ανά αίτημα που παράγουν μηνύματα πριν από την αρχικοποίηση. Η απλή απόκρυψη των μηνυμάτων δεν βοηθά σύμφωνα με το php.net.
  4. Διόρθωση, στην εφαρμογή, μοιραίων σφαλμάτων και υπερβάσεων χρόνου που μπορούν να προκληθούν από έξω, ακόμη και σε callbacks, μεταφορτώσεις και επεξεργασία εισαγωγών.

Η πραγματική θεραπεία είναι η ενημέρωση στην PHP 8.5.12, 8.4.27, 8.3.36 ή 8.2.35. Επειδή το σφάλμα βρίσκεται στον εκτελούμενο worker, γίνεται αποτελεσματική μόνο μετά από επανεκκίνηση όλων των διεργασιών· τα βήματα υπάρχουν στη σελίδα Επανεκκίνηση του PHP-FPM και όλων των workers.

Συμβουλή

Ελέγξτε μετά την επανεκκίνηση με ps -eo pid,lstart,cmd | grep php αν όλες οι διεργασίες PHP ξεκίνησαν μετά τη χρονική στιγμή της ενημέρωσης.

Τι σημαίνει ο κύκλος ζωής για τη λειτουργία και την ανάπτυξη

Από το μοντέλο προκύπτουν σαφείς κανόνες. Για τους διαχειριστές: μια ενημέρωση είναι αποτελεσματική μόνο όταν δεν εκτελείται πλέον καμία διεργασία με παλιό κώδικα. Κάθε τρόπος λειτουργίας με μακρόβιους workers χρειάζεται μια τεκμηριωμένη επανεκκίνηση. Για τους προγραμματιστές: ο κώδικας που εκτελείται σε τρόπους λειτουργίας worker δεν επιτρέπεται να αφήνει κατάσταση που αφορά το αίτημα σε στατικές μεταβλητές ή singletons, όπως δεδομένα χρήστη ή δικαιώματα. Και για τους δύο ισχύει: φυλάξτε τα logs από τις 20.10.2026, ώστε να μπορείτε αργότερα να κρίνετε αν συνέβη κάτι πριν από το patch.

Πώς βοηθά συγκεκριμένα η PHP SOS;

Με την υπηρεσία patch PHP εφαρμόζουμε το μοντέλο στην πράξη:

  • Προσδιορισμός του τρόπου λειτουργίας: Διαπιστώνουμε αν εκτελείται PHP-FPM, mod_php, LiteSpeed, FrankenPHP ή php-cgi και πώς διαχειρίζονται οι workers.
  • Ενημέρωση και επανεκκίνηση: Εγκαθιστούμε τη διορθωμένη έκδοση και επανεκκινούμε ελεγχόμενα κάθε worker.
  • Έλεγχος διεργασιών: Αποδεικνύουμε με βάση χρόνους εκκίνησης και εκδόσεις ότι δεν έχει μείνει καμία παλιά διεργασία.
  • Έλεγχος των τρόπων λειτουργίας worker: Στις μακρόβιες εφαρμογές εξετάζουμε αν η κατάσταση που αφορά το αίτημα επαναφέρεται σωστά.

Συχνές ερωτήσεις

Ξεκινά η PHP νέα διεργασία για κάθε αίτημα;

Στη γραμμή εντολών ναι, στη λειτουργία ιστού συνήθως όχι. Το PHP-FPM, ο Apache με mod_php, το LiteSpeed ή το FrankenPHP αφήνουν μια διεργασία να επεξεργάζεται πολλά αιτήματα το ένα μετά το άλλο. Ανάμεσα στα αιτήματα η PHP καθαρίζει την κατάσταση του αιτήματος.

Ποια είναι η διαφορά μεταξύ MINIT και RINIT;

Το MINIT εκτελείται μία φορά, όταν ξεκινά η διεργασία PHP, και δημιουργεί μόνιμες δομές. Το RINIT εκτελείται στην αρχή κάθε μεμονωμένου αιτήματος και προετοιμάζει ό,τι χρειάζεται μόνο αυτό το αίτημα.

Βοηθά το pm.max_requests κατά της CVE-2026-109619;

Το php.net δεν αναφέρει τις συχνότερες επανεκκινήσεις workers ως προσωρινό μέτρο. Η ρύθμιση περιορίζει μόνο πόσα αιτήματα επεξεργάζεται ένας worker πριν επανεκκινήσει. Απέναντι στην ευπάθεια βοηθά η ενημέρωση με επακόλουθη επανεκκίνηση όλων των workers.

Τι είναι ο τρόπος λειτουργίας worker του FrankenPHP;

Στον τρόπο λειτουργίας worker, η εφαρμογή εκκινεί μία φορά και παραμένει στη μνήμη. Αυτό εξοικονομεί χρόνο, αλλά οι στατικές μεταβλητές, τα globals και οι κρυφές μνήμες διατηρούνται ανάμεσα στα αιτήματα. Σύμφωνα με την τεκμηρίωση του FrankenPHP, η κατάσταση που αφορά το αίτημα πρέπει γι' αυτό να επαναφέρεται συνειδητά.

Πηγές

Άμεση επικοινωνία

Κάθε ώρα μετράει. Γράψτε μας τώρα.

Περιγράψτε σύντομα το σύστημα, την έκδοση PHP και ό,τι παρατηρείτε. Θα επικοινωνήσουμε μαζί σας, θα διευκρινίσουμε την κατάσταση και θα εργαστούμε εξ αποστάσεως μέχρι το περιστατικό να κλείσει και να τεκμηριωθεί.

Άμεση βοήθεια