Εν συντομία: Το SQL injection στην PHP το αποτρέπετε αξιόπιστα αν δεν ενσωματώνετε ποτέ τιμές σε κείμενα SQL, αλλά τις περνάτε πάντα μέσω Prepared Statements με placeholders. Για ονόματα στηλών και πινάκων, που δεν μπορούν να περαστούν ως placeholders, ισχύει μια σταθερή λίστα επιτρεπόμενων τιμών. Επιπλέον, ένας χρήστης βάσης δεδομένων με ελάχιστα δικαιώματα περιορίζει τη ζημιά αν παραμείνει παρ' όλα αυτά μια ευπάθεια.
Γιατί το SQL injection δεν πρέπει να χαθεί από το οπτικό πεδίο μετά το PHP2Shell
Τον Οκτώβριο του 2026 πολλά περιστρέφονται γύρω από το PHP2Shell, δύο κρίσιμες ευπάθειες στον διερμηνέα PHP, που διορθώνονται από τις 21.10.2026 στις PHP 8.5.12, 8.4.27, 8.3.36 και 8.2.35. Πολλές ομάδες ενημέρωσαν αυτές τις ημέρες διακομιστές και επανεκκίνησαν διεργασίες. Αυτό είναι σωστό και επείγον.
Μια τρέχουσα κατάσταση PHP δεν λέει όμως τίποτα για τον δικό σας κώδικα. Το SQL injection ανήκει εδώ και χρόνια στις συχνότερες αδυναμίες των εφαρμογών ιστού και καταγράφεται στο OWASP Top 10 στην κατηγορία Injection. Όποιος σχεδιάζει ούτως ή άλλως ελέγχους ασφάλειας, πρέπει να εξετάσει ταυτόχρονα και τις προσβάσεις στη βάση δεδομένων της δικής του εφαρμογής.
Τα καλά νέα: το SQL injection είναι μία από τις λίγες αδυναμίες που μπορούν να αποκλειστούν σχεδόν πλήρως με έναν απλό κανόνα που εφαρμόζεται συνεπώς.
Πώς προκύπτει το SQL injection στην PHP;
Το SQL injection προκύπτει όταν μια εφαρμογή αντιμετωπίζει εισόδους χρηστών ως τμήμα μιας εντολής SQL αντί για καθαρά δεδομένα. Τυπικό είναι το συναρμολόγημα ενός ερωτήματος από κομμάτια κειμένου και μεταβλητές. Αν ένας επισκέπτης μπορεί να καθορίσει το περιεχόμενο μιας τέτοιας μεταβλητής, μπορεί υπό συνθήκες να αλλάξει το νόημα της εντολής.
Οι συνέπειες εκτείνονται από την ανάγνωση ξένων δεδομένων πελατών, την αλλαγή τιμών ή δικαιωμάτων, έως τη διαγραφή ολόκληρων πινάκων. Ανάλογα με τη βάση δεδομένων και τα δικαιώματα, μια ευπάθεια μπορεί να επιτρέψει και προσβάσεις πέρα από τη βάση. Αν επηρεάζονται προσωπικά δεδομένα, μπορεί να προκύψει υποχρέωση γνωστοποίησης βάσει ΓΚΠΔ.
Είσοδοι δεν είναι μόνο τα πεδία φορμών. Και οι παράμετροι URL, τα cookies, οι κεφαλίδες HTTP, τα αρχεία που ανεβαίνουν, οι απαντήσεις ξένων διεπαφών και τα δεδομένα που είναι ήδη αποθηκευμένα στη δική σας βάση θεωρούνται μη αξιόπιστα.
Prepared Statements με PDO και mysqli
Τα Prepared Statements στέλνουν την εντολή SQL και τις τιμές χωριστά στη βάση δεδομένων. Οι τιμές δεν ερμηνεύονται ποτέ ως τμήμα της εντολής. Με το PDO ένα ασφαλές ερώτημα μοιάζει έτσι:
<?php
$pdo = new PDO($dsn, $benutzer, $passwort, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$stmt = $pdo->prepare('SELECT id, name, email FROM kunden WHERE id = :id AND status = :status');
$stmt->execute([
'id' => (int) $_GET['id'],
'status' => 'aktiv',
]);
$kunde = $stmt->fetch();
Η επιλογή PDO::ATTR_EMULATE_PREPARES => false φροντίζει ώστε η βάση δεδομένων να χρησιμοποιεί πραγματικά Prepared Statements, στο μέτρο που το υποστηρίζει ο driver. Με το mysqli ισχύει η ίδια αρχή:
<?php
$stmt = $mysqli->prepare('SELECT id, name FROM produkte WHERE kategorie = ? AND preis <= ?');
$stmt->bind_param('sd', $kategorie, $maxPreis);
$stmt->execute();
$ergebnis = $stmt->get_result();
Και οι λίστες για IN (...) μπορούν να αποδοθούν με ασφάλεια, αν δημιουργήσετε για κάθε τιμή ένα δικό της placeholder και περάσετε τις τιμές χωριστά.
<?php
$ids = array_map('intval', $ausgewaehlteIds);
$platzhalter = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT id, name FROM produkte WHERE id IN ($platzhalter)");
$stmt->execute($ids);
Ειδικές περιπτώσεις: ονόματα στηλών, ταξινόμηση και ωμή SQL
Τα placeholders λειτουργούν μόνο για τιμές, όχι για ονόματα πινάκων, ονόματα στηλών ή κατευθύνσεις ταξινόμησης. Ακριβώς εδώ προκύπτουν στην πράξη πολλές ευπάθειες, π.χ. σε ταξινομήσιμους πίνακες ή φίλτρα. Η λύση είναι μια σταθερή λίστα επιτρεπόμενων τιμών:
<?php
$erlaubteSpalten = ['name' => 'name', 'preis' => 'preis', 'datum' => 'erstellt_am'];
$spalte = $erlaubteSpalten[$_GET['sort'] ?? ''] ?? 'name';
$richtung = (($_GET['dir'] ?? '') === 'desc') ? 'DESC' : 'ASC';
$stmt = $pdo->query("SELECT id, name, preis FROM produkte ORDER BY $spalte $richtung");
Τα frameworks και τα CMS φέρνουν δικά τους εργαλεία. Στο WordPress τη δουλειά αυτή αναλαμβάνει το $wpdb->prepare(), στο Laravel και στο Symfony οι query builders και τα ORM. Μόλις χρησιμοποιήσετε εκεί ωμή SQL, π.χ. μέσω συναρτήσεων με «raw» στο όνομα, ισχύουν ξανά οι κανόνες αυτής της ενότητας. Υποδείξεις για το Laravel θα βρείτε στη σελίδα Ασφάλεια εφαρμογών Laravel.
Αναζητήστε στοχευμένα στον κώδικα κείμενα SQL στα οποία εισάγονται μεταβλητές. Μια απλή αρχή είναι η αναζήτηση των query( και ->raw σε συνδυασμό με $_GET, $_POST ή $_REQUEST.
Εντοπισμός του SQL injection στον υπάρχοντα κώδικα
Σε νέο λογισμικό ο κανόνας μπορεί να τηρηθεί από την αρχή. Δυσκολότερο είναι στις εφαρμογές που έχουν μεγαλώσει επί χρόνια. Εκεί αξίζει μια συστηματική αναζήτηση σημείων όπου κείμενα SQL συναρμολογούνται με μεταβλητές:
# Εντολές SQL στις οποίες εισάγονται μεταβλητές
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Άμεση χρήση δεδομένων αιτήματος σε συναρτήσεις ερωτημάτων
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"
Δεν είναι κάθε εύρημα ευπάθεια, π.χ. όταν η μεταβλητή περιέχει μόνο ένα σταθερό όνομα πίνακα. Κάθε σημείο που βρίσκετε πρέπει όμως να ελεγχθεί συνειδητά μία φορά και, αν χρειάζεται, να μετατραπεί σε Prepared Statements. Εργαλεία στατικής ανάλυσης κώδικα βοηθούν να εντοπίζονται τέτοια μοτίβα μόνιμα στο build pipeline, ώστε τα νέα σφάλματα να μη φτάνουν καν στην παραγωγή.
Περιορισμός της ζημιάς: δικαιώματα, μηνύματα σφάλματος, έλεγχος
Ο ασφαλής κώδικας είναι η πρώτη γραμμή άμυνας. Περαιτέρω επίπεδα περιορίζουν τη ζημιά σε περίπτωση που μια ευπάθεια διαφύγει:
- Ελάχιστα δικαιώματα: Ο χρήστης βάσης δεδομένων του ιστότοπου χρειάζεται συνήθως μόνο
SELECT,INSERT,UPDATEκαιDELETEστη δική του βάση, όχι δικαιώματα δημιουργίας χρηστών ή πρόσβασης σε άλλες βάσεις. - Κανένα μήνυμα σφάλματος προς τα έξω:
display_errors = Offστο php.ini. Τα σφάλματα της βάσης ανήκουν στο log και όχι στο πρόγραμμα περιήγησης. - Διαχωρισμός ανά εφαρμογή: Κάθε ιστότοπος λαμβάνει δικό του χρήστη βάσης δεδομένων.
- Διατήρηση των εξαρτήσεων επικαιροποιημένων: Ευπάθειες κρύβονται και σε βιβλιοθήκες, όπως δείχνει ο οδηγός composer audit.
- Έλεγχος από ειδικούς: Το code review και η αυτοματοποιημένη ανάλυση εντοπίζουν πολλά σημεία, ένα penetration test δείχνει τι είναι πράγματι προσβάσιμο απ' έξω.
-- Παράδειγμα: χρήστης βάσης δεδομένων μόνο με τα απαραίτητα δικαιώματα
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';
Μια συγγενής αδυναμία αφορά την εμφάνιση δεδομένων στο πρόγραμμα περιήγησης. Πώς την προστατεύετε, εξηγεί ο οδηγός Αποτροπή του Cross-Site Scripting στην PHP.
Πώς βοηθά συγκεκριμένα η PHP SOS;
- Ελέγχουμε συστηματικά τις προσβάσεις στη βάση δεδομένων της εφαρμογής σας για μη ασφαλή ερωτήματα.
- Ιεραρχούμε τα ευρήματα με βάση την προσβασιμότητα και τα επηρεαζόμενα δεδομένα και δείχνουμε συγκεκριμένες διορθώσεις.
- Φέρνουμε τα δικαιώματα της βάσης και τον χειρισμό σφαλμάτων σε ασφαλές επίπεδο.
- Δοκιμάζουμε ξανά μετά τη διόρθωση και τεκμηριώνουμε το αποτέλεσμα.
Έναν ανεξάρτητο έλεγχο περιγράφει η υπηρεσία Έλεγχος ασφάλειας και penetration test.
Συχνές ερωτήσεις
Αρκεί να επεξεργάζομαι τις εισόδους με addslashes ή real_escape_string;
Όχι, είναι επιρρεπές σε λάθη. Το escaping εξαρτάται από το σύνολο χαρακτήρων και το πλαίσιο και ξεχνιέται εύκολα σε ένα σημείο. Τα Prepared Statements διαχωρίζουν θεμελιωδώς την εντολή από τα δεδομένα και είναι γι' αυτό ο συνιστώμενος τρόπος.
Προστατεύει αυτόματα ένα ORM από το SQL injection;
Σε μεγάλο βαθμό, εφόσον χρησιμοποιείτε τις συναρτήσεις ερωτημάτων του ORM. Μόλις γράψετε ωμή SQL ή εισαγάγετε τιμές σε ταξινομήσεις και ονόματα στηλών, ισχύουν οι ίδιοι κανόνες όπως χωρίς ORM.
Βοηθά ένα Web Application Firewall ενάντια στο SQL injection;
Ένα WAF μπορεί να αναχαιτίσει γνωστά μοτίβα και να κερδίσει χρόνο. Δεν αντικαθιστά όμως ασφαλή κώδικα, επειδή οι κανόνες με βάση τα δεδομένα του αιτήματος μπορούν να παρακαμφθούν και μερικές φορές πλήττουν νόμιμες εισόδους.
Κλείνει η ενημέρωση PHP για το PHP2Shell και ευπάθειες SQL injection;
Όχι. Το PHP2Shell αφορά τον διερμηνέα PHP. Το SQL injection είναι σφάλμα στον κώδικα της εφαρμογής και παραμένει, όποια έκδοση PHP κι αν εκτελείται.