Εν συντομία: Για την ασφάλεια των sessions στην PHP μετρούν τέσσερα πράγματα: ασφαλείς ρυθμίσεις cookie, νέο ID session μετά από κάθε σύνδεση, χωριστοί και προστατευμένοι χώροι αποθήκευσης και μικρές διάρκειες. Λόγω του PHP2Shell πρέπει επιπλέον να έχει οριστεί στον διακομιστή το session.upload_progress.enabled=0, εφόσον δεν υπάρχει λόγος για αυτή τη λειτουργία.
Γιατί τα sessions είναι ελκυστικός στόχος
Ένα session θυμάται ότι ένας χρήστης είναι συνδεδεμένος. Τεχνικά, το πρόγραμμα περιήγησης λαμβάνει γι' αυτό ένα τυχαίο ID session σε ένα cookie και ο διακομιστής αποθηκεύει τα αντίστοιχα δεδομένα. Όποιος γνωρίζει αυτό το ID, είναι για την εφαρμογή ο ίδιος χρήστης, χωρίς κωδικό πρόσβασης και χωρίς δεύτερο παράγοντα.
Για τους διαχειριστές αυτό σημαίνει: μια αδύναμη διαχείριση sessions μπορεί να υπονομεύσει την προστασία της σύνδεσης, ακόμη και αν οι κωδικοί πρόσβασης και η σύνδεση δύο παραγόντων έχουν υλοποιηθεί σωστά. Αυτό είναι ιδιαίτερα κρίσιμο σε περιοχές διαχείρισης, λογαριασμούς πελατών και portals με προσωπικά δεδομένα.
Οι τυπικοί κίνδυνοι είναι η υποκλοπή του ID session (session hijacking), η υπόθεση ενός γνωστού ID πριν από τη σύνδεση (session fixation) και οι υπερβολικά μακρόχρονες συνεδρίες σε κοινόχρηστους υπολογιστές.
Ασφαλείς βασικές ρυθμίσεις στο php.ini
Οι σημαντικότερες τιμές μπορούν να οριστούν κεντρικά στο php.ini ή ανά ιστότοπο στο pool του FPM. Στο pool χρησιμοποιείτε php_admin_value ή php_admin_flag, ώστε η εφαρμογή να μην μπορεί να αντικαταστήσει τις τιμές.
; Βασική θωράκιση sessions
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"
; Υπόδειξη PHP2Shell από το php.net, στη συνέχεια επανεκκίνηση όλων των workers
session.upload_progress.enabled = 0
Το use_strict_mode φροντίζει ώστε η PHP να μην αποδέχεται άγνωστα ID που επιβάλλει το πρόγραμμα περιήγησης. Το cookie_httponly εμποδίζει τη JavaScript να διαβάσει το ID, γεγονός που περιορίζει τις συνέπειες του Cross-Site Scripting. Το cookie_secure στέλνει το cookie μόνο μέσω HTTPS. Το SameSite=Lax δυσκολεύει αιτήματα cross-site με έγκυρη συνεδρία· για καθαρά περιοχές διαχείρισης μπορεί να ταιριάζει το Strict.
Η διάρκεια των 1.800 δευτερολέπτων είναι ενδεικτική τιμή. Ορίστε την ανάλογα με την ανάγκη προστασίας: μικρότερη για διαχείριση και δεδομένα πελατών, μεγαλύτερη μόνο όπου απαιτείται λειτουργικά.
Σημειώστε ότι το gc_maxlifetime ορίζει μόνο από πότε επιτρέπεται να διαγράφονται παλιά δεδομένα session. Σε ορισμένες διανομές δεν καθαρίζει τα αρχεία session η ίδια η PHP, αλλά μια τακτική εργασία συστήματος. Ελέγξτε τότε ποια τιμή χρησιμοποιεί αυτή η εργασία, ώστε η ρύθμισή σας να ισχύει πράγματι. Για την αποσύνδεση λόγω αδράνειας μην βασίζεστε επιπλέον μόνο στη λειτουργία καθαρισμού, αλλά ελέγχετε τη χρονική στιγμή της τελευταίας ενέργειας μέσα στην ίδια την εφαρμογή, όπως δείχνει το παρακάτω παράδειγμα.
Ανανέωση του ID session μετά τη σύνδεση
Ο σημαντικότερος κανόνας στον κώδικα είναι να δημιουργείται νέο ID session σε κάθε αλλαγή δικαιωμάτων, δηλαδή κατά τη σύνδεση, κατά την αλλαγή σε λειτουργία διαχειριστή και κατά την αποσύνδεση. Το παρακάτω παράδειγμα δείχνει ένα καθαρό βασικό μοτίβο με password_verify():
<?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; // το ίδιο μήνυμα για λάθος διεύθυνση και λάθος κωδικό
}
session_regenerate_id(true); // νέο ID, απόρριψη του παλιού
$_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();
}
Συμπληρωματικά προστατεύετε τις φόρμες που αλλάζουν κάτι με ένα token CSRF. Δημιουργείται με bin2hex(random_bytes(32)), αποθηκεύεται στο session και συγκρίνεται κατά την υποβολή με hash_equals().
Χώροι αποθήκευσης και διαχωρισμός ενοικιαστών
Από προεπιλογή η PHP αποθηκεύει τα sessions ως αρχεία σε έναν κοινό κατάλογο. Σε διακομιστή με πολλούς ιστοτόπους αυτό μπορεί να σημαίνει ότι ένας παραβιασμένος ιστότοπος μπορεί να διαβάσει τα sessions ενός άλλου. Γι' αυτό σε κάθε ιστότοπο ανήκει ένα δικό του pool με δικό του χρήστη και δική του διαδρομή session.
; στο pool του FPM του αντίστοιχου ιστότοπου
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Ο κατάλογος πρέπει να ανήκει στον χρήστη του pool και να έχει δικαιώματα 0700. Πώς απομονώνετε επιπλέον τους ιστοτόπους μεταξύ τους, περιγράφει ο οδηγός open_basedir και απομόνωση ανά ιστότοπο. Αν τα sessions αποθηκεύονται σε Redis ή σε βάση δεδομένων, ισχύουν οι ίδιες αρχές: δική της πρόσβαση ανά εφαρμογή, καμία ανοιχτή πρόσβαση δικτύου.
Ασφάλεια sessions PHP και PHP2Shell
Οι ευπάθειες PHP2Shell CVE-2026-109618 και CVE-2026-109619 βρίσκονται στην ίδια την PHP. Ως ένα από τα προσωρινά μέτρα το php.net αναφέρει να οριστεί το session.upload_progress.enabled=0 στο php.ini, στο pool του FPM ή στη διαμόρφωση του διακομιστή και στη συνέχεια να επανεκκινηθούν όλοι οι workers. Ρητά όχι μέσω ini_set() ή ενός .user.ini.
Στις 23.10.2026 ακολούθησε μια υπόδειξη για το CVE-2026-115560. Μια πιθανή εκτέλεση κώδικα υπάρχει εκεί μόνο με έναν ασυνήθιστο συνδυασμό serialization των sessions, αλλαγής μέσω ini_set() πριν από το session_start() και ενεργού upload progress. Το προσωρινό μέτρο είναι η ίδια ρύθμιση, μέχρι την επόμενη τακτική έκδοση διορθώσεων.
Αυτή η ρύθμιση είναι προσωρινό μέτρο και όχι πλήρης προστασία. Αποφασιστική παραμένει η ενημέρωση σε διορθωμένη έκδοση PHP και η επακόλουθη επανεκκίνηση όλων των workers PHP.
Ελέγξτε μετά την αλλαγή ποια τιμή ισχύει πράγματι. Η γραμμή εντολών εμφανίζει συχνά διαφορετική διαμόρφωση από τον διακομιστή ιστού· αξιόπιστη είναι μια προστατευμένη σελίδα ελέγχου, που διαγράφεται στη συνέχεια, ή η ίδια η διαμόρφωση του pool.
Τυπικά λάθη στα sessions των εφαρμογών PHP
Πολλές αδυναμίες δεν προκύπτουν από ελλιπείς ρυθμίσεις, αλλά από τον τρόπο με τον οποίο οι εφαρμογές χρησιμοποιούν τα sessions. Αυτά τα μοτίβα πρέπει να τα ρωτάτε στοχευμένα στον δικό σας κώδικα και σε παρόχους υπηρεσιών:
- Υπερβολικά πολλά στο session: Κωδικοί πρόσβασης, πλήρη στοιχεία πληρωμής ή μεγάλα σύνολα δεδομένων δεν ανήκουν στο session. Αποθηκεύετε μόνο ό,τι χρειάζεται για την αντιστοίχιση, π.χ. το ID χρήστη και τους ρόλους.
- Δικαιώματα μόνο από το session: Το αν κάποιος είναι διαχειριστής πρέπει να ελέγχεται στη βάση δεδομένων σε κρίσιμες ενέργειες. Διαφορετικά τα δικαιώματα που έχουν αφαιρεθεί παραμένουν ενεργά μέχρι τη λήξη της συνεδρίας.
- ID session στη διεύθυνση: Τα ID σε URLs καταλήγουν σε logs, ιστορικά προγραμμάτων περιήγησης και στοιχεία referer. Τα
use_only_cookiesκαιuse_trans_sid = 0το αποτρέπουν. - Κανένα τέλος της συνεδρίας: Αν λείπει ένα timeout αδράνειας, ένας διαχειριστής παραμένει συνδεδεμένος απεριόριστα σε κοινόχρηστο υπολογιστή.
- Λανθασμένη αναγνώριση του HTTPS πίσω από proxy: Αν η κρυπτογράφηση τερματίζεται στον load balancer, η εφαρμογή πρέπει να το γνωρίζει. Διαφορετικά ορίζει ενδεχομένως cookies χωρίς τη σημαία
Secure.
Μετά από ένα περιστατικό ασφαλείας ανήκει επίσης στην αντιμετώπιση να απορριφθούν όλα τα υπάρχοντα sessions. Το απλούστερο είναι να διαγράψετε το περιεχόμενο του καταλόγου session ή να αλλάξετε το όνομα του session. Όλοι οι χρήστες πρέπει τότε να συνδεθούν ξανά και οι κλεμμένες συνεδρίες δεν έχουν καμία αξία.
Πώς βοηθά συγκεκριμένα η PHP SOS;
- Ελέγχουμε τις ρυθμίσεις sessions, τις σημαίες cookie και τους χώρους αποθήκευσης σε όλους τους ιστοτόπους ενός διακομιστή και τεκμηριώνουμε τις τιμές που ισχύουν πράγματι.
- Εφαρμόζουμε τα προσωρινά μέτρα PHP2Shell ανά pool, επανεκκινούμε ελεγχόμενα τους workers και αποδεικνύουμε το αποτέλεσμα.
- Διαχωρίζουμε ιστοτόπους με δικά τους pools και διαδρομές session στο πλαίσιο της θωράκισης διακομιστή.
- Για ιδιόκτητες υλοποιήσεις ελέγχουμε στον κώδικα τη σύνδεση, την αποσύνδεση και την αλλαγή session και προτείνουμε συγκεκριμένες αλλαγές.
Συχνές ερωτήσεις
Τι είναι το session hijacking;
Στο session hijacking ένας επιτιθέμενος καταλαμβάνει τη συνεδρία ενός συνδεδεμένου χρήστη αποκτώντας το ID session του. Με το ID είναι για την εφαρμογή ο συγκεκριμένος χρήστης, χωρίς να γνωρίζει τον κωδικό πρόσβασης. Ασφαλείς σημαίες cookie και μικρές διάρκειες το δυσκολεύουν σημαντικά.
Γιατί πρέπει να δημιουργείται νέο ID session μετά τη σύνδεση;
Αν το ID παραμένει ίδιο πριν και μετά τη σύνδεση, ένα ID που ήταν γνωστό εκ των προτέρων ή που έχει παρεισφρήσει θα μπορούσε να εξακολουθεί να ισχύει μετά την είσοδο. Το session_regenerate_id(true) δημιουργεί νέο ID και απορρίπτει το παλιό.
Μπορώ να απενεργοποιήσω το session.upload_progress.enabled με ini_set();
Όχι, αυτό δεν αρκεί σύμφωνα με το php.net. Η ρύθμιση ανήκει στο php.ini, στο pool του FPM ή στη διαμόρφωση του διακομιστή και όχι στο ini_set() ή σε ένα .user.ini. Στη συνέχεια πρέπει να επανεκκινηθούν όλοι οι workers της PHP.
Πού πρέπει να αποθηκεύονται τα αρχεία session;
Σε έναν κατάλογο εκτός webroot, τον οποίο επιτρέπεται να διαβάζει μόνο ο χρήστης του αντίστοιχου pool PHP. Σε διακομιστές με πολλούς ιστοτόπους κάθε ιστότοπος πρέπει να έχει δικό του κατάλογο session.