Nette Documentation Preview

syntax
Κίνδυνοι ασφαλείας
******************

.[perex]
Οι βάσεις δεδομένων συχνά περιέχουν ευαίσθητα δεδομένα και επιτρέπουν επικίνδυνες λειτουργίες. Η Nette Database παρέχει μια σειρά από χαρακτηριστικά ασφαλείας. Ωστόσο, είναι ζωτικής σημασίας να κατανοήσετε τη διαφορά μεταξύ ασφαλών και μη ασφαλών API.


Εγχείρηση SQL .[#toc-sql-injection]
===================================

Η έγχυση SQL είναι ο σοβαρότερος κίνδυνος ασφάλειας όταν εργάζεστε με βάσεις δεδομένων. Συμβαίνει όταν η μη ελεγχόμενη είσοδος του χρήστη γίνεται μέρος ενός ερωτήματος SQL. Ένας εισβολέας μπορεί να εισάγει τις δικές του εντολές SQL, αποκτώντας ή τροποποιώντας δεδομένα στη βάση δεδομένων.

```php
// ❌ UNSAFE CODE - ευάλωτος σε έγχυση SQL
$database->query("SELECT * FROM users WHERE name = '$_GET[name]'");

// Ο επιτιθέμενος μπορεί να εισάγει κάτι σαν: ' OR '1'='1
// Το ερώτημα που θα προκύψει θα είναι:
// OR '1'='1'.
// Αυτό επιστρέφει όλους τους χρήστες!
```

Το ίδιο ισχύει και για τον Database Explorer:

```php
// ❌ UNSAFE CODE
$table->where('name = ' . $_GET['name']);
$table->where("name = '$_GET[name]'");
```


Ασφαλή παραμετροποιημένα ερωτήματα .[#toc-safe-parameterized-queries]
=====================================================================

Ο ασφαλής τρόπος εισαγωγής τιμών σε ερωτήματα SQL είναι μέσω παραμετροποιημένων ερωτημάτων. Η Nette Database παρέχει διάφορους τρόπους για τη χρήση τους.


Ερωτηµατικά σηµάδια συµπλήρωσης .[#toc-placeholder-question-marks]
------------------------------------------------------------------

Η απλούστερη μέθοδος είναι η χρήση ερωτηματικών τύπου placeholder:

```php
// ✅ Ασφαλή παραμετροποιημένα ερωτήματα
$database->query('SELECT * FROM users WHERE name = ?', $_GET['name']);

// ✅ Ασφαλής συνθήκη στην Εξερεύνηση
$table->where('name = ?', $_GET['name']);
```

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

.[warning]
Οι τιμές πρέπει να είναι κλιμακωτού τύπου (`string`, `int`, `float`, `bool`) ή `null`. Αν, για παράδειγμα, `$_GET['name']` είναι ένας πίνακας, η Nette Database θα συμπεριλάβει όλα τα στοιχεία του στο ερώτημα SQL, πράγμα που μπορεί να είναι ανεπιθύμητο.


Πίνακες τιμών .[#toc-value-arrays]
----------------------------------

Για τις ρήτρες `INSERT`, `UPDATE` ή `WHERE`, μπορούμε να χρησιμοποιήσουμε πίνακες τιμών:

```php
// ✅ Ασφαλής ΕΙΣΑΓΩΓΗ
$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 .[#toc-array-keys-are-not-a-safe-api]
=================================================================================

Ενώ οι τιμές στους πίνακες είναι ασφαλείς, δεν ισχύει το ίδιο για τα κλειδιά:

```php
// ❌ 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` με τον ακόλουθο τρόπο:

```php
$_GET = ['salary >', 100000];   // αρχίζει τον καθορισμό των μισθολογικών κλιμακίων
```

Το κύριο πρόβλημα, ωστόσο, είναι ότι οι συνθήκες `WHERE` υποστηρίζουν εκφράσεις SQL στα κλειδιά:

```php
// Νόμιμη χρήση τελεστών σε κλειδιά
$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**.


Λευκή λίστα στηλών .[#toc-whitelisting-columns]
-----------------------------------------------

Αν θέλετε να επιτρέψετε στους χρήστες να επιλέγουν στήλες, χρησιμοποιείτε πάντα μια λευκή λίστα:

```php
// ✅ Ασφαλής επεξεργασία - μόνο επιτρεπόμενες στήλες
$allowedColumns = ['name', 'email', 'active'];
$values = array_intersect_key($_GET, array_flip($allowedColumns));

$database->query('INSERT INTO users', $values);
```


Δυναμικά αναγνωριστικά .[#toc-dynamic-identifiers]
==================================================

Για δυναμικά ονόματα πινάκων και στηλών, χρησιμοποιήστε τον αντικαταστάτη `?name`:

```php
// ✅ Ασφαλής χρήση αξιόπιστων αναγνωριστικών
$table = 'users';
$column = 'name';
$database->query('SELECT ?name FROM ?name', $column, $table);

// ❌ UNSAFE - Ποτέ μην χρησιμοποιείτε είσοδο χρήστη
$database->query('SELECT ?name FROM users', $_GET['column']);
```

Το σύμβολο `?name` πρέπει να χρησιμοποιείται μόνο για αξιόπιστες τιμές που ορίζονται στον κώδικα της εφαρμογής. Για τιμές που παρέχονται από τον χρήστη, χρησιμοποιήστε πάλι μια λευκή λίστα.

Κίνδυνοι ασφαλείας

Οι βάσεις δεδομένων συχνά περιέχουν ευαίσθητα δεδομένα και επιτρέπουν επικίνδυνες λειτουργίες. Η 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 πρέπει να χρησιμοποιείται μόνο για αξιόπιστες τιμές που ορίζονται στον κώδικα της εφαρμογής. Για τιμές που παρέχονται από τον χρήστη, χρησιμοποιήστε πάλι μια λευκή λίστα.