Κίνδυνοι ασφαλείας
Οι βάσεις δεδομένων συχνά περιέχουν ευαίσθητα δεδομένα και επιτρέπουν επικίνδυνες λειτουργίες. Η Nette Database παρέχει μια σειρά από χαρακτηριστικά ασφαλείας. Ωστόσο, είναι ζωτικής σημασίας να κατανοήσετε τη διαφορά μεταξύ ασφαλών και μη ασφαλών API.
Εγχείρηση SQL
Η έγχυση SQL είναι ο σοβαρότερος κίνδυνος ασφάλειας όταν εργάζεστε με βάσεις δεδομένων. Συμβαίνει όταν η μη ελεγχόμενη είσοδος του χρήστη γίνεται μέρος ενός ερωτήματος SQL. Ένας εισβολέας μπορεί να εισάγει τις δικές του εντολές SQL, αποκτώντας ή τροποποιώντας δεδομένα στη βάση δεδομένων.
// ❌ UNSAFE CODE - ευάλωτος σε έγχυση SQL
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");
// Ο επιτιθέμενος μπορεί να εισάγει κάτι σαν: ' OR '1'='1
// Το ερώτημα που θα προκύψει θα είναι:
// OR '1'='1'.
// Αυτό επιστρέφει όλους τους χρήστες!
Το ίδιο ισχύει και για τον Database Explorer:
// ❌ UNSAFE CODE
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
Ασφαλή παραμετροποιημένα ερωτήματα
Ο ασφαλής τρόπος εισαγωγής τιμών σε ερωτήματα SQL είναι μέσω παραμετροποιημένων ερωτημάτων. Η Nette Database παρέχει διάφορους τρόπους για τη χρήση τους.
Ερωτηµατικά σηµάδια συµπλήρωσης
Η απλούστερη μέθοδος είναι η χρήση ερωτηματικών τύπου placeholder:
// ✅ Ασφαλή παραμετροποιημένα ερωτήματα
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);
// ✅ Ασφαλής συνθήκη στην Εξερεύνηση
$table->where('name = ?', $_GET['name']);
Το ίδιο ισχύει και για όλες τις άλλες μεθόδους της Εξερεύνησης βάσης δεδομένων που επιτρέπουν την εισαγωγή εκφράσεων με ερωτηματικά και παραμέτρους με εικονικά ερωτηματικά.
Οι τιμές πρέπει να είναι κλιμακωτού τύπου (string
,
int
, float
, bool
) ή null
. Αν, για παράδειγμα,
$_GET['name']
είναι ένας πίνακας, η Nette Database θα συμπεριλάβει όλα τα
στοιχεία του στο ερώτημα SQL, πράγμα που μπορεί να είναι ανεπιθύμητο.
Πίνακες τιμών
Για τις ρήτρες INSERT
, UPDATE
ή WHERE
, μπορούμε να
χρησιμοποιήσουμε πίνακες τιμών:
// ✅ Ασφαλής ΕΙΣΑΓΩΓΗ
$database->query('INSERT INTO users', [
'name' => $_GET['name'],
'email' => $_GET['email'],
]);
// ✅ Ασφαλής ΕΝΗΜΕΡΩΣΗ
$database->query('UPDATE users SET', [
'name' => $_GET['name'],
'email' => $_GET['email'],
], 'WHERE id = ?', $_GET['id']);
Η Nette Database διαφυλάσσει αυτόματα όλες τις τιμές που περνούν μέσω παραμετροποιημένων ερωτημάτων. Ωστόσο, πρέπει να διασφαλίσουμε τον σωστό τύπο δεδομένων των παραμέτρων.
Τα κλειδιά συστοιχίας δεν είναι ασφαλές API
Ενώ οι τιμές στους πίνακες είναι ασφαλείς, δεν ισχύει το ίδιο για τα κλειδιά:
// ❌ UNSAFE CODE - τα κλειδιά μπορεί να περιέχουν SQL injection
$database->query('INSERT INTO users', $_GET);
$database->query('SELECT * FROM users WHERE', $_GET);
$table->where($_GET);
Για τις εντολές INSERT
και UPDATE
, αυτό είναι ένα κρίσιμο
ελάττωμα ασφαλείας – ένας εισβολέας θα μπορούσε να εισάγει ή να
τροποποιήσει οποιαδήποτε στήλη στη βάση δεδομένων. Για παράδειγμα, θα
μπορούσε να ορίσει το is_admin = 1
ή να εισάγει αυθαίρετα δεδομένα σε
ευαίσθητες στήλες.
Σε συνθήκες WHERE
, αυτό είναι ακόμη πιο επικίνδυνο επειδή
επιτρέπει SQL enumeration – μια τεχνική για τη σταδιακή ανάκτηση
πληροφοριών σχετικά με τη βάση δεδομένων. Ένας επιτιθέμενος θα
μπορούσε να επιχειρήσει να εξερευνήσει τους μισθούς των υπαλλήλων
εισάγοντας στο $_GET
με τον ακόλουθο τρόπο:
$_GET = ['salary >', 100000]; // αρχίζει τον καθορισμό των μισθολογικών κλιμακίων
Το κύριο πρόβλημα, ωστόσο, είναι ότι οι συνθήκες WHERE
υποστηρίζουν εκφράσεις SQL στα κλειδιά:
// Νόμιμη χρήση τελεστών σε κλειδιά
$table->where([
'age > ?' => 18,
'ROUND(score, ?) > ?' => [2, 75.5],
]);
// ❌ UNSAFE: ο επιτιθέμενος μπορεί να εισάγει τη δική του SQL
$_GET = ['1) UNION SELECT name, salary FROM users WHERE (is_admin = ?' => 1];
$table->where($_GET); // επιτρέπει στον επιτιθέμενο να αποκτήσει μισθούς διαχειριστή
Αυτό είναι και πάλι SQL injection.
Λευκή λίστα στηλών
Αν θέλετε να επιτρέψετε στους χρήστες να επιλέγουν στήλες, χρησιμοποιείτε πάντα μια λευκή λίστα:
// ✅ Ασφαλής επεξεργασία - μόνο επιτρεπόμενες στήλες
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));
$database->query('INSERT INTO users', $values);
Δυναμικά αναγνωριστικά
Για δυναμικά ονόματα πινάκων και στηλών, χρησιμοποιήστε τον
αντικαταστάτη ?name
:
// ✅ Ασφαλής χρήση αξιόπιστων αναγνωριστικών
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);
// ❌ UNSAFE - Ποτέ μην χρησιμοποιείτε είσοδο χρήστη
$database->query('SELECT ?name FROM users', $_GET['column']);
Το σύμβολο ?name
πρέπει να χρησιμοποιείται μόνο για αξιόπιστες
τιμές που ορίζονται στον κώδικα της εφαρμογής. Για τιμές που
παρέχονται από τον χρήστη, χρησιμοποιήστε πάλι μια λευκή λίστα.