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

Πώς η PHP επεξεργάζεται τις μεταφορτώσεις αρχείων

Ανάμεσα στο κλικ στη μεταφόρτωση και στον κώδικά σας μεσολαβεί μια ολόκληρη αλυσίδα επεξεργασίας στην PHP. Η σελίδα εξηγεί το multipart/form-data, τα προσωρινά αρχεία, το $_FILES, τα όρια και τη σχέση με το PHP2Shell.

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

Εν συντομία: Όταν ένα πρόγραμμα περιήγησης μεταφορτώνει ένα αρχείο, στέλνει ένα αίτημα POST σε μορφή multipart/form-data, στο οποίο τα πεδία της φόρμας και τα περιεχόμενα των αρχείων βρίσκονται ως ξεχωριστά τμήματα το ένα μετά το άλλο. Η PHP αναλύει η ίδια αυτό το αίτημα, γράφει κάθε τμήμα αρχείου σε ένα προσωρινό αρχείο στο upload_tmp_dir, καταχωρίζει τα στοιχεία στο $_FILES και μόνο τότε ξεκινά το σενάριό σας. Το σενάριο μετακινεί το αρχείο με το move_uploaded_file()· ό,τι δεν μετακινηθεί, η PHP το διαγράφει στο τέλος του αιτήματος.

Η διαδρομή ενός αρχείου από το πρόγραμμα περιήγησης ως το σενάριο

Για όσους αποφασίζουν, το πιο σημαντικό είναι το εξής: μια μεταφόρτωση δεν είναι διαδικασία που αφορά μόνο την εφαρμογή. Ένα μεγάλο μέρος της δουλειάς γίνεται στον διερμηνέα της PHP, πριν εμπλακεί καν ο κώδικας του CMS ή του ηλεκτρονικού καταστήματος. Γι' αυτό τα σφάλματα σε αυτό το τμήμα της PHP αφορούν κάθε ιστότοπο, ανεξάρτητα από το αν διαθέτει φόρμα μεταφόρτωσης.

Η διαδικασία με μια ματιά:

  1. Υποβάλλεται η φόρμα με method="POST" και enctype="multipart/form-data".
  2. Ο διακομιστής ιστού δέχεται το αίτημα και το περνά στην PHP, ανάλογα με τον τρόπο λειτουργίας μέσω PHP-FPM, mod_php ή άλλης διεπαφής.
  3. Η PHP ελέγχει όρια όπως το post_max_size και διαχωρίζει το περιεχόμενο με βάση μια ακολουθία διαχωρισμού, το λεγόμενο boundary.
  4. Τα κανονικά πεδία καταλήγουν στο $_POST, τα τμήματα αρχείων γράφονται σε προσωρινά αρχεία και περιγράφονται στο $_FILES.
  5. Μόνο τώρα ξεκινά το σενάριο, ελέγχει το αρχείο και το μετακινεί στον προορισμό του.
  6. Στο τέλος του αιτήματος η PHP διαγράφει όλα τα προσωρινά αρχεία που δεν μετακινήθηκαν.

multipart/form-data: η δομή ενός αιτήματος μεταφόρτωσης

Ένα αίτημα μεταφόρτωσης είναι απλό κείμενο με ενσωματωμένα δεδομένα αρχείων. Η κεφαλίδα Content-Type δηλώνει το boundary και στο περιεχόμενο αυτή η ακολουθία χαρακτήρων διαχωρίζει τα επιμέρους τμήματα. Κάθε τμήμα έχει δικές του κεφαλίδες με το όνομα του πεδίου και, ενδεχομένως, το όνομα του αρχείου. Ένα έντονα συντομευμένο, αβλαβές παράδειγμα:

POST /bewerbung HTTP/1.1
Content-Type: multipart/form-data; boundary=----trenner42

------trenner42
Content-Disposition: form-data; name="vorname"

Erika
------trenner42
Content-Disposition: form-data; name="lebenslauf"; filename="lebenslauf.pdf"
Content-Type: application/pdf

%PDF-1.7 ... Dateiinhalt ...
------trenner42--

Όλα τα στοιχεία αυτού του κειμένου προέρχονται από τον αποστολέα, ακόμη και το όνομα αρχείου και το Content-Type του τμήματος. Η PHP τα μεταφέρει στο $_FILES, αλλά δεν ελέγχει αν είναι εύλογα.

Ο πίνακας $_FILES

Για ένα πεδίο με όνομα lebenslauf, το $_FILES['lebenslauf'] περιέχει σύμφωνα με το εγχειρίδιο της PHP τα εξής κλειδιά:

Κλειδί Περιεχόμενο Αξιόπιστο;
name αρχικό όνομα αρχείου στον αποστολέα όχι
full_path διαδρομή που δηλώνει το πρόγραμμα περιήγησης, από την PHP 8.1.0 όχι
type τύπος MIME σύμφωνα με το πρόγραμμα περιήγησης, δεν ελέγχεται από την PHP όχι
size μέγεθος σε byte υπολογίζεται από την PHP
tmp_name διαδρομή του προσωρινού αρχείου στον διακομιστή ορίζεται από την PHP
error κωδικός σφάλματος της επεξεργασίας της μεταφόρτωσης ορίζεται από την PHP

Σε πεδία με αγκύλες στο όνομα, για παράδειγμα bilder[], κάθε κλειδί περιέχει έναν πίνακα με μία εγγραφή ανά αρχείο. Η εφαρμογή διατρέχει τότε, για παράδειγμα, το $_FILES['bilder']['error'] και προσπελαύνει με τον ίδιο δείκτη τα tmp_name και name.

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

Όρια: οι οδηγίες php.ini για τις μεταφορτώσεις

Αυτές οι οδηγίες ελέγχουν την επεξεργασία των μεταφορτώσεων. Οι προεπιλεγμένες τιμές προέρχονται από το εγχειρίδιο της PHP και ισχύουν όταν δεν τις παρακάμπτει κάποιο php.ini.

Οδηγία Προεπιλογή Τροποποιήσιμη σε
file_uploads 1 μόνο στη διαμόρφωση συστήματος
upload_tmp_dir κενό (προσωρινός κατάλογος συστήματος) μόνο στη διαμόρφωση συστήματος
upload_max_filesize 2M php.ini, pool, διαμόρφωση καταλόγου
post_max_size 8M php.ini, pool, διαμόρφωση καταλόγου
max_file_uploads 20 php.ini, pool, διαμόρφωση καταλόγου
enable_post_data_reading 1 php.ini, pool, διαμόρφωση καταλόγου

Το post_max_size πρέπει να είναι μεγαλύτερο από το upload_max_filesize και το memory_limit πρέπει σύμφωνα με το εγχειρίδιο, κατά κανόνα, να είναι μεγαλύτερο από το post_max_size. Αν ο κατάλογος upload_tmp_dir δεν είναι εγγράψιμος, η PHP καταφεύγει στον προσωρινό κατάλογο του συστήματος. Με το open_basedir πρέπει να επιτρέπεται ο προσωρινός κατάλογος, διαφορετικά οι μεταφορτώσεις αποτυγχάνουν. Τιμές που χρησιμοποιούνται στην πράξη και τις συνέπειές τους περιγράφει το άρθρο Λογικός περιορισμός των post_max_size και upload_max_filesize.

Σωστή αξιολόγηση των κωδικών σφάλματος

Το πεδίο error δείχνει τι συνέβη κατά την επεξεργασία. Οι σταθερές εκτείνονται από το UPLOAD_ERR_OK (0) μέσω των UPLOAD_ERR_INI_SIZE (1), UPLOAD_ERR_FORM_SIZE (2), UPLOAD_ERR_PARTIAL (3) και UPLOAD_ERR_NO_FILE (4) ως τα UPLOAD_ERR_NO_TMP_DIR (6), UPLOAD_ERR_CANT_WRITE (7) και UPLOAD_ERR_EXTENSION (8).

<?php
declare(strict_types=1);

$datei = $_FILES['lebenslauf'] ?? null;
if ($datei === null) {
    exit('Keine Upload-Daten, eventuell wurde post_max_size überschritten.');
}

$meldung = match ($datei['error']) {
    UPLOAD_ERR_OK => null,
    UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'Datei zu groß.',
    UPLOAD_ERR_NO_FILE => 'Keine Datei gewählt.',
    default => 'Upload fehlgeschlagen.',
};

if ($meldung === null && is_uploaded_file($datei['tmp_name'])) {
    $ziel = '/srv/app/uploads/' . bin2hex(random_bytes(16)) . '.pdf';
    move_uploaded_file($datei['tmp_name'], $ziel);
}

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

Μεταφορτώσεις και PHP2Shell: τι σημαίνουν τα προσωρινά μέτρα

Οι ευπάθειες CVE-2026-109618 και CVE-2026-109619 του PHP2Shell αφορούν την επεξεργασία γύρω από το αίτημα στον διερμηνέα της PHP. Γι' αυτό το php.net αναφέρει ως προσωρινά μέτρα, μεταξύ άλλων, post_max_size = 8M (χωρίς μεταφορτώσεις) ή 32M (με μεταφορτώσεις), upload_max_filesize χαμηλότερα, καθώς και session.upload_progress.enabled=0. Αυτές οι τιμές ανήκουν στο php.ini, στο pool του FPM ή στη διαμόρφωση του διακομιστή και στη συνέχεια πρέπει να γίνει επανεκκίνηση όλων των workers.

Επιπλέον, ειδικοί συγγραφείς προτείνουν να αποκλείονται τα αιτήματα multipart/form-data εκτός των πραγματικών διαδρομών μεταφόρτωσης, στον proxy ή στο WAF. Πρόκειται για ανεπίσημο μέτρο, με τον κίνδυνο να σπάσουν φόρμες· τη στάθμιση περιγράφει το άρθρο Φιλτράρισμα αιτημάτων μεταφόρτωσης στην άκρη. Κανένα από αυτά τα μέτρα δεν αντικαθιστά την ενημέρωση στην PHP 8.5.12, 8.4.27, 8.3.36 ή 8.2.35.

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

  • Εντοπίζουμε ποιοι από τους ιστότοπούς σας χρειάζονται πράγματι μεταφορτώσεις και σε ποιες διαδρομές.
  • Ορίζουμε τα όρια και τα προσωρινά μέτρα ανά pool, επανεκκινούμε όλους τους workers και δοκιμάζουμε τις φόρμες.
  • Ελέγχουμε τους καταλόγους μεταφόρτωσης για εκτελέσιμα αρχεία και αποκλείουμε την εκτέλεση σεναρίων στο πλαίσιο της θωράκισης διακομιστή.
  • Εγκαθιστούμε την ενημέρωση της PHP και τεκμηριώνουμε το επίπεδο ενημέρωσης ανά ιστότοπο.

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

Πού αποθηκεύει η PHP αρχικά τα αρχεία που μεταφορτώνονται;

Σε έναν προσωρινό κατάλογο. Από προεπιλογή είναι ο προσωρινός κατάλογος του συστήματος· με το upload_tmp_dir μπορεί να αλλάξει. Αν το αρχείο δεν μετακινηθεί μέσα στο σενάριο, η PHP το διαγράφει στο τέλος του αιτήματος.

Μπορώ να εμπιστεύομαι το πεδίο type του $_FILES;

Όχι. Τον τύπο MIME τον δηλώνει το πρόγραμμα περιήγησης και σύμφωνα με το εγχειρίδιο η PHP δεν τον ελέγχει. Και τα name και full_path προέρχονται από τον αποστολέα. Τον πραγματικό τύπο του αρχείου τον προσδιορίζετε στην πλευρά του διακομιστή με βάση το περιεχόμενο.

Γιατί το $_FILES είναι άδειο, ενώ στάλθηκε αρχείο;

Συνήθως ολόκληρο το αίτημα ήταν μεγαλύτερο από το post_max_size. Τότε η PHP αδειάζει πλήρως τα $_POST και $_FILES, χωρίς κωδικό σφάλματος στον πίνακα. Άλλες αιτίες είναι ένα enctype που λείπει από τη φόρμα ή απενεργοποιημένα file_uploads.

Βοηθά κατά του PHP2Shell η απαγόρευση των μεταφορτώσεων στην εφαρμογή;

Όχι. Η επεξεργασία των δεδομένων του αιτήματος γίνεται στην PHP, πριν εκτελεστεί ο κώδικας της εφαρμογής. Αποτελεσματικά είναι η ενημέρωση της PHP και η επανεκκίνηση όλων των workers· τα στενότερα όρια αποτελούν μόνο προσωρινό μέτρο.

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

Το upload_max_filesize περιορίζει κάθε μεμονωμένο αρχείο, ενώ το post_max_size ολόκληρο το περιεχόμενο του αιτήματος, μαζί με όλα τα αρχεία και τα πεδία. Γι' αυτό το post_max_size πρέπει να είναι μεγαλύτερο από το upload_max_filesize.

Πηγές

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

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

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

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