Εν ολίγοις: Κάθε ιστότοπος σε έναν διακομιστή πρέπει να έχει δικό του pool PHP-FPM με δικό του χρήστη συστήματος και δικό του socket. Ο διαχωρισμός γίνεται όμως αποτελεσματικός μόνο με τις λεπτομέρειες: δικαιώματα socket μόνο για τον διακομιστή ιστού, security.limit_extensions σε .php, καμία δημόσια προσβάσιμη σελίδα κατάστασης, δικά του όρια για διεργασίες και χρόνο εκτέλεσης και χωριστά logs. Από το ίδιο το PHP2Shell προστατεύει μόνο η ενημέρωση· τα pools περιορίζουν πόσο μακριά φτάνει μια επίθεση.
Ένας διακομιστής, πολλοί ιστότοποι, ένας κοινός κίνδυνος
Σε πολλούς διακομιστές έχει αναπτυχθεί με τα χρόνια κάτι που κανείς δεν σχεδίασε έτσι. Ο ιστότοπος της εταιρείας, το κατάστημα, μια σελίδα καμπάνιας του 2019 και ένα δοκιμαστικό σύστημα λειτουργούν δίπλα-δίπλα, συχνά με τον ίδιο χρήστη και στο ίδιο pool. Αν παραβιαστεί η ξεχασμένη σελίδα καμπάνιας, εκτίθενται και τα δεδομένα πελατών του καταστήματος.
Το PHP-FPM, ο διαχειριστής διεργασιών FastCGI της PHP, το λύνει με pools: ομάδες διεργασιών worker με δικές τους ρυθμίσεις. Πώς λειτουργεί η βασική αρχή, εξηγεί το PHP-FPM: διαχειριστής διεργασιών, pools και workers. Τη δημιουργία διαχωρισμένων pools με δικό τους χρήστη, δικό τους φάκελο temp και open_basedir περιγράφει ο οδηγός open_basedir και απομόνωση ανά ιστότοπο. Αυτό το άρθρο πάει ένα βήμα πιο πέρα: πώς ελέγχετε τα υπάρχοντα pools και ποιες ρυθμίσεις κάνουν τον διαχωρισμό ανθεκτικό;
Σημαντικό για την τοποθέτηση: στο PHP2Shell οι ευπάθειες CVE-2026-109618 και CVE-2026-109619 βρίσκονται στον διερμηνέα PHP, τον οποίο μοιράζονται όλα τα pools μιας έκδοσης PHP. Ένα ξεχωριστό pool δεν εμποδίζει την εκμετάλλευση. Αποφασίζει όμως αν ένας επιτιθέμενος θα φτάσει στη συνέχεια έναν ιστότοπο ή όλους.
Pools PHP-FPM με δικό τους χρήστη: έλεγχος της υπάρχουσας κατάστασης
Προτού αλλάξετε κάτι, αποκτήστε μια εικόνα. Οι παρακάτω εντολές μόνο διαβάζουν. Οι διαδρομές και οι αριθμοί εκδόσεων διαφέρουν ανά διανομή:
# Όλα τα pools με χρήστη και socket
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf
# Pools που εκτελούνται και οι χρήστες τους
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'
# Sockets και τα δικαιώματά τους
ls -l /run/php/
# Έλεγχος της διαμόρφωσης για σφάλματα (δεν αλλάζει τίποτα)
sudo php-fpm8.3 -t
Προσέξτε τρία μοτίβα: περισσότεροι ιστότοποι στο ίδιο pool, pools που εκτελούνται ως www-data ή ακόμη και ως root, και sockets στα οποία μπορούν να γράφουν όλοι οι χρήστες. Καθένα από αυτά υπονομεύει τον διαχωρισμό.
Αν στον διακομιστή εκτελούνται πολλές εκδόσεις PHP, καθεμία έχει τη δική της υπηρεσία FPM με δικούς της φακέλους pool. Ελέγξτε όλες, όχι μόνο την έκδοση που ρυθμίσατε τελευταία. Ειδικά οι παλιές εκδόσεις με ξεχασμένα pools αποτελούν κίνδυνο, επειδή συχνά ούτε συντηρούνται ούτε επανεκκινούνται μετά τις ενημερώσεις. Υποδείξεις υπάρχουν στο Πολλές εκδόσεις PHP σε έναν διακομιστή. Καταγράψτε το αποτέλεσμα σε έναν μικρό πίνακα: ιστότοπος, pool, χρήστης, socket, έκδοση PHP. Αυτή η επισκόπηση αποτελεί ταυτόχρονα τη βάση για κάθε μεταγενέστερη αλλαγή.
Οι σημαντικότερες ρυθμίσεις ανά pool
Η παρακάτω επισκόπηση στηρίζεται στη διαμόρφωση FPM του εγχειριδίου της PHP. Οι τιμές για διεργασίες και χρόνους εξαρτώνται από το φορτίο και την εφαρμογή σας:
| Ρύθμιση | Σκοπός | Σύσταση |
|---|---|---|
user, group |
χρήστης των workers | ξεχωριστός χρήστης ανά ιστότοπο, ποτέ root |
listen.owner, listen.group, listen.mode |
δικαιώματα στο socket | μόνο η ομάδα του διακομιστή ιστού, λειτουργία 0660 |
listen.acl_users |
πρόσβαση μέσω ACL αντί για ιδιοκτήτη | καταχωρίστε μόνο τον χρήστη του διακομιστή ιστού |
security.limit_extensions |
εκτελέσιμες καταλήξεις | περιορισμός σε .php (προεπιλογή .php .phar) |
clear_env |
εκκαθάριση μεταβλητών περιβάλλοντος | αφήστε την προεπιλογή yes |
pm.max_children |
μέγιστος αριθμός workers | περιορισμός ώστε ένας ιστότοπος να μην εξαντλεί τον διακομιστή |
request_terminate_timeout |
διακοπή αιτημάτων που κρέμονται | ορισμός ανάλογα με το μακρύτερο θεμιτό αίτημα |
pm.status_path, ping.path |
σελίδα κατάστασης και ping | ορίστε τα μόνο αν χρειάζονται και προστατέψτε τα εσωτερικά |
slowlog, request_slowlog_timeout |
καταγραφή αργών αιτημάτων | ξεχωριστό αρχείο ανά pool |
Ένα απόσπασμα για ένα σκληρυμένο pool μοιάζει ως εξής:
; /etc/php/8.3/fpm/pool.d/shop.conf (απόσπασμα)
[shop]
user = shop
group = shop
listen = /run/php/shop.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 60s
security.limit_extensions = .php
clear_env = yes
slowlog = /var/log/php/shop.slow.log
request_slowlog_timeout = 10s
php_admin_value[error_log] = /var/log/php/shop.error.log
Ρυθμίσεις όπως το memory_limit ή το session.upload_progress.enabled τις ορίζετε ανά pool με php_admin_value και php_admin_flag. Σύμφωνα με το εγχειρίδιο, αυτές οι τιμές δεν μπορούν να αντικατασταθούν με ini_set(). Περισσότερα στο php_admin_value: κλείδωμα ρυθμίσεων κατά της αντικατάστασης.
Σωστή τοποθέτηση των σελίδων κατάστασης και της εναλλαγής workers
Τα pm.status_path και ping.path είναι ενεργά μόνο όταν έχουν οριστεί. Η σελίδα κατάστασης δείχνει αιτήματα που εκτελούνται, διεργασίες και φόρτο. Για την παρακολούθηση είναι χρήσιμη, αν είναι όμως δημόσια προσβάσιμη αποτελεί περιττή πηγή πληροφοριών για επιτιθέμενους. Προωθήστε αυτές τις διαδρομές στον διακομιστή ιστού μόνο για εσωτερικές διευθύνσεις. Πώς συνεργάζονται με ασφάλεια το Nginx και το PHP-FPM, περιγράφει το Ασφάλιση του Nginx με PHP-FPM.
Το pm.max_requests ορίζει μετά από πόσα αιτήματα επανεκκινείται ένας worker· η προεπιλογή είναι 0, δηλαδή ποτέ. Μια τιμή μεγαλύτερη από μηδέν βοηθά κατά της αργά αυξανόμενης κατανάλωσης μνήμης. Ως προστασία κατά του PHP2Shell δεν προορίζεται: το php.net δεν την αναφέρει στα μεταβατικά μέτρα και τονίζει ότι μόνο η ενημέρωση προστατεύει πλήρως. Μετά την ενημέρωση ισχύει επιπλέον: όλοι οι workers όλων των pools πρέπει να επανεκκινηθούν, επειδή οι παλιές διεργασίες αλλιώς συνεχίζουν να εκτελούνται με τον ευάλωτο κώδικα.
Διατηρείτε ένα πρότυπο για νέα pools και δημιουργείτε με αυτό κάθε νέο ιστότοπο. Έτσι ο διαχωρισμός προκύπτει αυτόματα, αντί να προστίθεται εκ των υστέρων χρόνια αργότερα.
Τυπικά λάθη κατά τη μετάβαση
Το συχνότερο λάθος είναι ένα νέο pool που εκτελείται μεν με δικό του χρήστη, τα αρχεία του όμως ανήκουν ακόμη στον www-data. Τότε το pool δεν μπορεί να γράψει εκεί που πρέπει και κάποιος ορίζει υπερβολικά γενναιόδωρα δικαιώματα. Διευκρινίστε εκ των προτέρων ποιοι φάκελοι πρέπει να είναι εγγράψιμοι. Το δεύτερο λάθος είναι οι προγραμματισμένες εργασίες που εκτελούνται ακόμη με τον παλιό χρήστη και δημιουργούν αρχεία με λάθος ιδιοκτήτη. Το τρίτο είναι ένας κοινός φάκελος session για όλα τα pools, που καταργεί ξανά τον διαχωρισμό.
Δοκιμάστε μετά από κάθε μετάβαση τη σύνδεση, τα uploads, τις κρυφές μνήμες και τις προγραμματισμένες εργασίες του ιστοτόπου και παρακολουθήστε το log σφαλμάτων του pool.
Πώς βοηθά συγκεκριμένα η PHP SOS;
- Καταγράφουμε όλα τα pools, τους χρήστες, τα sockets και τις ρυθμίσεις και δείχνουμε πού μοιράζονται σήμερα οι ιστότοποι διεργασίες ή δικαιώματα.
- Δημιουργούμε για κάθε ιστότοπο ένα σκληρυμένο pool, με δικαιώματα socket, όριο καταλήξεων, όρια πόρων και δικά του logs.
- Προστατεύουμε τις σελίδες κατάστασης, επανεκκινούμε ελεγχόμενα όλους τους workers και δοκιμάζουμε κάθε ιστότοπο.
- Στο πλαίσιο της σκλήρυνσης διακομιστή τεκμηριώνουμε τον διαχωρισμό ως πρότυπο για μελλοντικά έργα.
Συχνές ερωτήσεις
Γιατί να έχει κάθε ιστότοπος δικό του pool PHP-FPM;
Ένα ξεχωριστό pool με ξεχωριστό χρήστη συστήματος διαχωρίζει τους ιστοτόπους σε επίπεδο διεργασιών και δικαιωμάτων αρχείων. Αν παραβιαστεί ένας ιστότοπος, ο κώδικας δεν μπορεί χωρίς άλλο να διαβάσει τα αρχεία των υπολοίπων. Επιπλέον, όρια και ρυθμίσεις μπορούν να οριστούν χωριστά για κάθε ιστότοπο.
Προστατεύει ένα ξεχωριστό pool από το PHP2Shell;
Όχι. Οι ευπάθειες βρίσκονται στον ίδιο τον διερμηνέα PHP, τον οποίο μοιράζονται όλα τα pools. Προστασία προσφέρει μόνο η ενημέρωση σε διορθωμένη έκδοση με επανεκκίνηση όλων των workers. Τα διαχωρισμένα pools περιορίζουν όμως πόσο μακριά φτάνει μια επιτυχημένη επίθεση.
Τι κάνει το security.limit_extensions;
Σύμφωνα με το εγχειρίδιο της PHP, η ρύθμιση περιορίζει ποιες καταλήξεις αρχείων εκτελεί το PHP-FPM ως κύριο σενάριο. Η προεπιλογή είναι .php και .phar. Όποιος την περιορίζει σε .php εμποδίζει, για παράδειγμα, αρχεία εικόνας που ανεβαίνουν να εκτελεστούν ως PHP, σε περίπτωση που ο διακομιστής ιστού έχει ρυθμιστεί λανθασμένα.
Είναι επικίνδυνη η σελίδα κατάστασης του PHP-FPM;
Αποκαλύπτει λεπτομέρειες για διεργασίες και αιτήματα που εκτελούνται. Αν είναι δημόσια προσβάσιμη χωρίς προστασία, διευκολύνει τον προσανατολισμό των επιτιθέμενων. Σύμφωνα με το εγχειρίδιο, είναι ενεργή μόνο όταν έχει οριστεί το pm.status_path. Τότε ο διακομιστής ιστού πρέπει να περιορίζει τη διαδρομή σε εσωτερικές διευθύνσεις.