Και η ελάχιστη βοήθεια θα ήταν από τη λειτουργία που αναπτύξατε. PHP: \"Προσπάσματα\". Σύνταξη ερωτημάτων mysql, κάθετες, διαφυγή εισαγωγικών Αναζήτηση σε κώδικα

συμβολοσειρά εισαγωγικών (11)

Προσπαθώ να βρω τον καλύτερο τρόπο για να γράψω ερωτήματα. Καταλαβαίνω επίσης τη σημασία του να είσαι συνεπής. Μέχρι στιγμής χρησιμοποιούσα τυχαία μονά εισαγωγικά, διπλά εισαγωγικά και backtick χωρίς καμία πραγματική σκέψη.

$query = "ΕΙΣΑΓΩΓΗ ΤΙΜΩΝ ΣΤΟ Πίνακα (id, col1, col2) (NULL, val1, val2)";

Επίσης, στο παραπάνω παράδειγμα, θεωρήστε ότι οι "πίνακας", "col[n]" και "val[n]" μπορούν να είναι μεταβλητές.

Ποιο είναι το πρότυπο για αυτό; Τι κάνεις?

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

Απαντήσεις

Τώρα ας υποθέσουμε ότι χρησιμοποιείτε μεταβλητή άμεσης ανάρτησης στο ερώτημα MySQL και στη συνέχεια χρησιμοποιήστε την ως εξής:

$query = "INSERT INTO `table` (`id`, `name`, `email`) VALUES (" ".$_POST["id"]." ", " ".$_POST["name"]." ", " ".$_POST["email"].." ")";

Αυτή είναι η καλύτερη πρακτική για τη χρήση μεταβλητών PHP στη MySQL.

Κυρίως στη Mysql, αυτοί οι τύποι αναγνωριστικών χρησιμοποιούνται σε ερωτήματα ` , " , " και ().

    " ή " χρησιμοποιήστε για να συμπεριλάβετε μια συμβολοσειρά ως τιμή "01/26/2014 00:00:00" ή "01/26/2014 00:00:00" . Αυτό το αναγνωριστικό χρησιμοποιείται μόνο για τη συνάρτηση συμβολοσειράς "01/26/2014 00:00:00", όπως now() ή sum ,max .

    ` χρησιμοποιήστε για να συμπεριλάβετε έναν πίνακα ή πίνακα πίνακα, π.χ. επιλέξτε στήλη_όνομα από όνομα_πίνακα όπου id = "2"

    () χρησιμοποιούνται μόνο για να περικλείουν απλώς τμήματα ενός ερωτήματος, για παράδειγμα, επιλέξτε το όνομα_στήλης από το όνομα_του πίνακα όπου (id = "2" και το φύλο = "αρσενικό") ή το όνομα = "rakesh.

Εκτός από όλες τις (καλά επεξηγημένες) απαντήσεις, δεν αναφέρθηκε καμία παρακάτω και έρχομαι συχνά σε αυτό το Q&A.

Με λίγα λόγια; Η MySQL πιστεύει ότι θέλετε να κάνετε μαθηματικάστον δικό σας πίνακα/στήλη και ερμηνεύστε παύλες όπως "email" ως email .

Άρνηση ευθύνης.Έτσι σκέφτηκα να το προσθέσω ως απάντηση "FYI" για όσους είναι εντελώς νέοι στην εργασία με βάσεις δεδομένων και που μπορεί να μην κατανοούν τους τεχνικούς όρους που έχουν ήδη περιγραφεί.

(Υπάρχουν καλές απαντήσεις παραπάνω σχετικά με τη φύση SQL της ερώτησής σας, αλλά αυτό μπορεί επίσης να είναι σχετικό εάν είστε νέος στην PHP.)

Ίσως είναι σημαντικό να σημειωθεί ότι η PHP αντιμετωπίζει διαφορετικά τα μονά και τα διπλά εισαγωγικά...

Οι συμβολοσειρές με ένα εισαγωγικό είναι "κυριολεκτικές" και είναι πολλές χορδές WYSIWYG. Οι συμβολοσειρές με διπλά εισαγωγικά ερμηνεύονται από την PHP για πιθανή αντικατάσταση μεταβλητής (οι αναδρομικές αναφορές στην PHP δεν είναι ακριβώς συμβολοσειρές, εκτελούν την εντολή στο φλοιό και επιστρέφουν το αποτέλεσμα).

$foo = "bar"; echo "υπάρχει ένα $foo"? // Υπάρχει ένα $foo echo "υπάρχει ένα $foo"; // Υπάρχει μια γραμμή ηχώ `ls -l`; // ... μια λίστα καταλόγου

Εάν οι πίνακες και οι τιμές cols είναι μεταβλητές, τότε υπάρχουν δύο τρόποι:

Με διπλά εισαγωγικά "" το πλήρες ερώτημα είναι:

$query = "INSERT INTO $table_name (id, $col1, $col2) ΤΙΜΕΣ (NULL, "$val1", "$val2")";

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") ΤΙΜΕΣ (NULL, "".$val1.", "".$val2."" ) ";

Με μονά εισαγωγικά "" :

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") VALUES (NULL, ".$val1.", ".$val2.")";

Χρησιμοποιήστε backtick `` όταν το όνομα στήλης/τιμής είναι παρόμοιο με μια δεσμευμένη λέξη-κλειδί MySQL.

Σημείωση.Εάν καθορίσετε ένα όνομα στήλης με όνομα πίνακα, χρησιμοποιήστε backtick όπως αυτό:

"όνομα_πίνακα" . "όνομα_στήλης".<- Примечание: исключить. из задних клещей.

Τα Backtick θα πρέπει να χρησιμοποιούνται για αναγνωριστικά πινάκων και στηλών, αλλά χρειάζονται μόνο όταν το αναγνωριστικό είναι μια δεσμευμένη λέξη-κλειδί MySQL ή όταν το αναγνωριστικό περιέχει χαρακτήρες διαστήματος ή χαρακτήρες εκτός του περιορισμένου συνόλου (δείτε παρακάτω). Συχνά συνιστάται να αποφεύγετε τη χρήση δεσμευμένων λέξεων-κλειδιών ως αναγνωριστικά στηλών ή πινάκων, εάν είναι δυνατόν, για να αποφύγετε το πρόβλημα της προσφοράς.

Θα πρέπει να χρησιμοποιούνται μεμονωμένα εισαγωγικά για τιμές συμβολοσειρών, όπως στη λίστα VALUES(). Τα διπλά εισαγωγικά υποστηρίζονται από τη MySQL και για τιμές συμβολοσειρών, αλλά τα μεμονωμένα εισαγωγικά είναι ευρύτερα αποδεκτά από άλλα RDBMS, επομένως είναι καλή ιδέα να χρησιμοποιείτε μονά εισαγωγικά αντί για διπλά εισαγωγικά.

Η MySQL αναμένει επίσης ότι οι κυριολεκτικές τιμές DATE και DATETIME θα αναφέρονται ως συμβολοσειρές με ένα εισαγωγικό, όπως "2001-01-01 00:00:00" . Για περισσότερες πληροφορίες, ανατρέξτε στην τεκμηρίωση της βιβλιογραφίας Ημερομηνία και ώρα, ιδιαίτερα εναλλακτικές λύσεις στη χρήση της παύλας - ως διαχωριστικό τμημάτων στις συμβολοσειρές ημερομηνίας.

Έτσι, χρησιμοποιώντας το παράδειγμά σας, θα έκανα διπλό cast της συμβολοσειράς PHP και θα χρησιμοποιούσα μονά εισαγωγικά για τις τιμές "val1", "val2". Το NULL είναι λέξη-κλειδί MySQL και μη-τιμή και επομένως δεν χρησιμοποιείται.

Κανένα από αυτά τα αναγνωριστικά πίνακα ή στήλης δεν είναι δεσμευμένες λέξεις ή δεν χρησιμοποιεί χαρακτήρες που απαιτούν παράθεση, αλλά ούτως ή άλλως τα παρέθεσα ανάποδα (περισσότερα για αυτό αργότερα...).

Οι συναρτήσεις που σχετίζονται με το RDBMS (όπως το NOW() στη MySQL) δεν πρέπει να αναφέρονται, αν και τα ορίσματά τους υπόκεινται στους ίδιους κανόνες ή κανόνες αναφοράς που έχουν ήδη αναφερθεί.

Backtick(`) πίνακας & στήλη ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ─ ΕΙΣΑΓΩΓΗ ΣΤΟΝ `πίνακα` (`id`, `col1`, `col2`, `date`, `updated`) ΤΙΜΕΣ (NULL, "val1", "val2", "2001-01-01", NOW())"; Λέξη-κλειδί χωρίς εισαγωγικά ─────┴┴┴┘ │ │ │ │ │ │ │││││ Μονά εισαγωγικά (") Συμβολοσειρές ─││ ─ ───┴── ┴────┘ │ │ │││││ Μονό εισαγωγικά (") ΗΜΕΡΟΜΗΝΙΑ ──────── ─ ───┴── Συνάρτηση χωρίς εισαγωγικά ────────────────────── ──┴┴┴┴┘

Μεταβλητή παρεμβολή

Τα μοτίβα αναφοράς για τις μεταβλητές δεν αλλάζουν, αν και αν σκοπεύετε να παρεμβάλετε μεταβλητές απευθείας σε μια συμβολοσειρά, πρέπει να είναι διπλά εισαγωγικά στην PHP. Απλώς βεβαιωθείτε ότι έχετε διαφύγει σωστά τις μεταβλητές για χρήση στην SQL. (Συνιστάται η χρήση ενός API που υποστηρίζει προετοιμασμένες δηλώσεις ως άμυνα έναντι της ένεσης SQL.)

// Το ίδιο πράγμα με ορισμένες αντικαταστάσεις μεταβλητών // Εδώ, ένα όνομα πίνακα μεταβλητής $table αναφέρεται με οπισθογραφικά εισαγωγικά, και οι μεταβλητές // στη λίστα VALUES είναι με ένα εισαγωγικό $query = "INSERT INTO «$τραπέζι».("id", "col1", "col2", "date") ΤΙΜΕΣ (NULL, "$val1", "$val2", "$date")";

Έτοιμες δηλώσεις

Όταν εργάζεστε με έτοιμες δηλώσεις, συμβουλευτείτε την τεκμηρίωση για να προσδιορίσετε εάν πρέπει να συμπεριληφθούν τα πληρωτικά δηλώσεων. Τα πιο δημοφιλή API που είναι διαθέσιμα σε PHP, PDO και MySQLi περιλαμβάνουν ανεξουσιοδότητος placeholders, όπως τα περισσότερα έτοιμα API οδηγιών σε άλλες γλώσσες:

// Παράδειγμα ΠΟΠ με επώνυμες παραμέτρους, χωρίς εισαγωγικά $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VALUES (:id, :col1, :col2, :date)" ; // Παράδειγμα MySQLi με ? parameters, unquoted $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) ΤΙΜΕΣ (?, ?, ?, ?)";

Σύμβολα που επιστρέφουν μια πίσω αναφορά σε αναγνωριστικά:

Για παράδειγμα:

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

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

Τώρα σχετικά με τα διπλά εισαγωγικά και τα μεμονωμένα εισαγωγικά (ο Michael το ανέφερε ήδη αυτό).

Αλλά για να ορίσετε την τιμή, πρέπει να χρησιμοποιήσετε μονά ή διπλά εισαγωγικά. Ας δούμε ένα άλλο παράδειγμα.

INSERT INTO "Tablename" ("id, "title") ΤΙΜΕΣ (NULL, title1);

Εδώ ξέχασα επίτηδες να τυλίξω τον τίτλο1 σε εισαγωγικά. Ο διακομιστής θα δέχεται τώρα τον τίτλο1 ως όνομα στήλης (δηλ. αναγνωριστικό). Επομένως, για να υποδείξετε ότι αυτή είναι μια τιμή, πρέπει να χρησιμοποιήσετε διπλά ή μονά εισαγωγικά.

INSERT INTO `Tablename` (`id, `title`) ΤΙΜΕΣ (NULL, "title1");

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

$query = "INSERT INTO `table` (`id`, `col1`, `col2`) ΤΙΜΕΣ (NULL, "$val1", "$val2"");

Τώρα, χρησιμοποιώντας διπλά εισαγωγικά στην PHP, θα κάνετε τις μεταβλητές $val1 και $val2 να χρησιμοποιούν τις τιμές τους, δημιουργώντας έτσι ένα έγκυρο ερώτημα. αρέσει

$val1 = "η τιμή μου 1"; $val2 = "η τιμή μου 2"; $query = "INSERT INTO `table` (`id`, `col1`, `col2`) ΤΙΜΕΣ (NULL, "$val1", "$val2"");

ΕΙΣΑΓΩΓΗ ΣΤΟΝ `πίνακα` (`id`, `col1`, `col2`) ΤΙΜΕΣ (NULL, "η τιμή μου 1", "η τιμή μου 2")

Υπήρχαν πολλές χρήσιμες απαντήσεις εδώ, που γενικά καταλήγουν σε δύο σημεία.

  1. Τα BACKTICKS (`) χρησιμοποιούνται γύρω από τα ονόματα αναγνωριστικών.
  2. ΜΟΝΑ ΑΠΟΦΑΣΙΚΑ (") χρησιμοποιούνται γύρω από τιμές.

Και όπως είπε ο @MichaelBerkowski

Τα Backtick θα πρέπει να χρησιμοποιούνται για αναγνωριστικά πινάκων και στηλών, αλλά χρειάζονται μόνο όταν το αναγνωριστικό είναι μια δεσμευμένη λέξη-κλειδί MySQL ή όταν το αναγνωριστικό περιέχει χαρακτήρες διαστήματος ή χαρακτήρες εκτός του περιορισμένου συνόλου (δείτε παρακάτω). Συχνά συνιστάται να αποφεύγετε τη χρήση δεσμευμένων λέξεων-κλειδιών ως αναγνωριστικά στηλών ή πινάκων, εάν είναι δυνατόν, για να αποφύγετε το πρόβλημα της προσφοράς.

Υπάρχει περίπτωση που το αναγνωριστικό δεν μπορεί να είναι δεσμευμένη λέξη-κλειδίή περιέχουν κενού χαρακτήρεςή χαρακτήρες εκτός του περιορισμένου συνόλουαλλά σίγουρα απαιτούν backlinks γύρω τους.

Το 123E10 είναι ένα έγκυρο αναγνωριστικό όνομα, αλλά και ένα έγκυρο κυριολεκτικό ΑΚΕΡΑΙΟ.

[Χωρίς να υπεισέλθω σε λεπτομέρειες πώς θα αποκτούσατε ένα τέτοιο όνομα] Ας υποθέσουμε ότι θέλω να δημιουργήσω έναν προσωρινό πίνακα που ονομάζεται 123456e6.

Κανένα ΣΦΑΛΜΑ στα backticks.

DB > δημιουργία προσωρινού πίνακα `123456e6` (χαρακτήρας `id` (8)); Ερώτημα ΟΚ, επηρεάζονται 0 σειρές (0,03 δευτ.)

ΣΦΑΛΜΑ εάν δεν χρησιμοποιείτε επανακλήσεις.

DB > δημιουργία προσωρινού πίνακα 123451e6 (χαρακτήρας `id` (8)); ΣΦΑΛΜΑ 1064 (42000): Έχετε ένα σφάλμα στη σύνταξη SQL. ελέγξτε το εγχειρίδιο που αντιστοιχεί στην έκδοση του διακομιστή MariaDB για τη σωστή σύνταξη που θα χρησιμοποιήσετε κοντά στο "123451e6 (`id` char (8))" στη γραμμή 1

Ωστόσο, το 123451a6 είναι ένα καλό όνομα αναγνωριστικού (χωρίς backticks).

DB > δημιουργία προσωρινού πίνακα 123451a6 (χαρακτήρας `id` (8)); Ερώτημα ΟΚ, επηρεάζονται 0 σειρές (0,03 δευτ.)

Αυτό οφείλεται αποκλειστικά στο ότι το 1234156e6 είναι επίσης εκθετικός αριθμός.

Θα πρέπει να χρησιμοποιούνται μεμονωμένα εισαγωγικά για τιμές συμβολοσειρών, όπως στη λίστα VALUES().

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

Όταν συνδυάζονται με PHP και MySQL, τα διπλά εισαγωγικά και τα μεμονωμένα εισαγωγικά απλοποιούν σημαντικά τον χρόνο σύνταξης ερωτημάτων.

Υπάρχουν δύο τύποι εισαγωγικών στη MySQL:

  1. " για να συμπεριλάβουμε κυριολεκτικά συμβολοσειρά
  2. ` να περιλαμβάνει αναγνωριστικά όπως ονόματα πινάκων και στηλών

Και μετά υπάρχει "αυτή είναι μια ειδική περίπτωση. Μπορεί να χρησιμοποιηθεί για έναςαπό τους παραπάνω στόχους κάθε φορά ανάλογα με το sql_mode του διακομιστή:

  1. ΠροκαθορισμένοΟ "χαρακτήρας" μπορεί να χρησιμοποιηθεί για την ένθεση κυριολεκτικών συμβολοσειρών "
  2. Στη λειτουργία ANSI_QUOTES " το σύμβολο μπορεί να χρησιμοποιηθεί για να περιλαμβάνει αναγνωριστικά, ANSI_QUOTES

Το ακόλουθο ερώτημα θα παράγει διαφορετικά αποτελέσματα (ή σφάλματα) ανάλογα με τη λειτουργία SQL:

ΕΠΙΛΕΞΤΕ "στήλη" ΑΠΟ τον πίνακα WHERE foo = "bar"

ANSI_QUOTES απενεργοποιημένο

Το ερώτημα θα επιλέξει την κυριολεκτική συμβολοσειρά "στήλη" όπου η στήλη foo είναι ίση με τη συμβολοσειρά "bar"

Ενεργοποιήθηκε ANSI_QUOTES

Το ερώτημα θα επιλέξει στήλη στήλης όπου η στήλη foo είναι ίση με στήλη

Πότε να χρησιμοποιείται

  • Σας προτείνω να αποφύγετε τη χρήση του ", ώστε ο κώδικάς σας να μην εξαρτάται από τις λειτουργίες SQL
  • Να συμπεριλαμβάνετε πάντα αναγνωριστικά καθώς αυτή είναι καλή πρακτική (αρκετές ερωτήσεις σχετικά με το SO συζητούν αυτό)

Υπάρχει σαφής διαφορά μεταξύ της χρήσης του " " και του " " .

Όταν το " " χρησιμοποιείται παντού, δεν υπάρχει "μεταμόρφωση ή μετάφραση". Τυπώνεται ως έχει.

Με το " ", οτιδήποτε περιβάλλει "μεταφράζεται ή μετατρέπεται" στην αξία του.

Αυτό που εννοώ με τη μετάφραση/μετατροπή είναι το εξής: οτιδήποτε περιέχεται σε μεμονωμένα εισαγωγικά δεν θα "μεταφραστεί" στις τιμές τους. Θα γίνουν δεκτά επειδή είναι μέσα σε εισαγωγικά. Παράδειγμα: a=23 , και μετά η ηχώ "$a" θα δημιουργήσει $a σε τυπική έξοδο. Ενώ η ηχώ "$a" θα παράγει 23 σε τυπική έξοδο.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — Διαφεύγει ειδικούς χαρακτήρες σε μια συμβολοσειρά για χρήση σε μια πρόταση SQL

Περιγραφή

mysql_real_escape_string (συμβολοσειρά $unescaped_string [, πόρος $link_identifier = NULL]): χορδή

Αποφεύγει από ειδικούς χαρακτήρες στο unescaped_string, λαμβάνοντας υπόψη το τρέχον σύνολο χαρακτήρων της σύνδεσης, ώστε να είναι ασφαλές να το τοποθετήσετε σε mysql_query(). Εάν πρόκειται να εισαχθούν δυαδικά δεδομένα, πρέπει να χρησιμοποιηθεί αυτή η συνάρτηση.

mysql_real_escape_string() καλεί τη συνάρτηση βιβλιοθήκης της MySQL mysql_real_escape_string, η οποία προσαρτά ανάστροφες κάθετες στους ακόλουθους χαρακτήρες: \x00, \n, \r, \ , " , " και \x1a.

Αυτή η συνάρτηση πρέπει πάντα (με ελάχιστες εξαιρέσεις) να χρησιμοποιείται για την ασφάλεια των δεδομένων πριν από την αποστολή ερωτήματος στη MySQL.

Προσοχή

Ασφάλεια: το προεπιλεγμένο σύνολο χαρακτήρων

Το σύνολο χαρακτήρων πρέπει να οριστεί είτε σε επίπεδο διακομιστή είτε με τη λειτουργία API mysql_set_charset()για να επηρεάσει mysql_real_escape_string() . Για περισσότερες πληροφορίες, ανατρέξτε στην ενότητα των εννοιών για τα σύνολα χαρακτήρων.

Παράμετροι

unescaped_string

Η χορδή που πρέπει να ξεφύγει.

Αναγνωριστικό_σύνδεσμου

Η σύνδεση MySQL. Εάν το αναγνωριστικό συνδέσμου δεν έχει καθοριστεί, ο τελευταίος σύνδεσμος άνοιξε από mysql_connect()υποτίθεται. Εάν δεν βρεθεί τέτοιος σύνδεσμος, θα προσπαθήσει να δημιουργήσει έναν σαν να mysql_connect()είχε κληθεί χωρίς επιχειρήματα. Εάν δεν βρεθεί ή δεν δημιουργηθεί σύνδεση, α E_ΠΡΟΕΙΔΟΠΟΙΗΣΗδημιουργείται σφάλμα επιπέδου.

Αξίες επιστροφής

Επιστρέφει τη συμβολοσειρά διαφυγής ή ΨΕΥΔΗΣσε λάθος.

Λάθη/Εξαιρέσεις

Η εκτέλεση αυτής της λειτουργίας χωρίς σύνδεση MySQL θα εκπέμπει επίσης E_ΠΡΟΕΙΔΟΠΟΙΗΣΗεπίπεδο σφαλμάτων PHP. Εκτελέστε αυτήν τη λειτουργία μόνο με έγκυρη σύνδεση MySQL.

Παραδείγματα

Παράδειγμα #1 Απλό mysql_real_escape_string() παράδειγμα

//Συνδέω-συωδεομαι
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
Ή die(mysql_error());

//Ερώτηση
$query = sprintf ( "SELECT * FROM users WHERE user="%s" AND password="%s"",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>

Παράδειγμα #2 mysql_real_escape_string() απαιτεί ένα παράδειγμα σύνδεσης

Αυτό το παράδειγμα δείχνει τι συμβαίνει εάν δεν υπάρχει σύνδεση MySQL κατά την κλήση αυτής της συνάρτησης.

Το παραπάνω παράδειγμα θα παράγει κάτι παρόμοιο με:

Προειδοποίηση: mysql_real_escape_string(): Δεν υπάρχει τέτοιο αρχείο ή κατάλογος στη γραμμή 5 /this/test/script.php Προειδοποίηση: mysql_real_escape_string(): Δεν ήταν δυνατή η δημιουργία συνδέσμου προς τον διακομιστή στο /this/test/script.php στη γραμμή 5 bool(false) string(41) "SELECT * FROM actors WHERE last_name = """

Παράδειγμα #3 Ένα παράδειγμα επίθεσης SQL Injection

// Δεν ελέγξαμε το $_POST["password"], θα μπορούσε να είναι οτιδήποτε ήθελε ο χρήστης! Για παράδειγμα:
$_POST [ "username" ] = "aidan" ;
$_POST [ "password" ] = "" Ή ""="" ;

// Ζητήστε τη βάση δεδομένων για να ελέγξετε εάν υπάρχουν χρήστες που ταιριάζουν
$query = ( $_POST [ "όνομα χρήστη" ]) " AND password=" ( $_POST [ "password" ]) "" ;
mysql_query ($query);

// Αυτό σημαίνει ότι το ερώτημα που αποστέλλεται στη MySQL θα είναι:
echo $query ;
?>

Το ερώτημα που στάλθηκε στη MySQL:

Αυτό θα επέτρεπε σε οποιονδήποτε να συνδεθεί χωρίς έγκυρο κωδικό πρόσβασης.

Σημειώσεις

Απαιτείται σύνδεση MySQL πριν από τη χρήση mysql_real_escape_string() διαφορετικά σφάλμα επιπέδου E_ΠΡΟΕΙΔΟΠΟΙΗΣΗδημιουργείται και ΨΕΥΔΗΣεπιστρέφεται. Εάν το link_identifier δεν έχει οριστεί, χρησιμοποιείται η τελευταία σύνδεση MySQL.

Σημείωση: mysql_real_escape_string() δεν ξεφεύγει % και _ . Αυτοί είναι χαρακτήρες μπαλαντέρ στη MySQL εάν συνδυαστούν με ΑΡΕΣΕΙ, ΧΟΡΗΓΗΣΗ, ή ΑΝΑΚΑΛΩ.

πριν 8 χρόνια

Απλώς μια μικρή συνάρτηση που μιμείται την αρχική mysql_real_escape_string αλλά δεν χρειάζεται ενεργή σύνδεση mysql. Θα μπορούσε να εφαρμοστεί ως στατική συνάρτηση σε μια κλάση βάσης δεδομένων. Ελπίζουμε ότι θα βοηθήσει κάποιον.

συνάρτηση mysql_escape_mimic ($inp) (
if(is_array($inp))
return array_map (__METHOD__ , $inp );

If(!empty($inp ) && is_string ($inp )) (
return str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """ , "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"", "\\"" , "\\Z" ), $inp );
}

Επιστροφή $inp ;
}
?>

πριν από 13 χρόνια

Σημειώστε ότι το mysql_real_escape_string δεν προϋποθέτει ανάστροφες κάθετες στα \x00, \n, \r και \x1a όπως αναφέρεται στην τεκμηρίωση, αλλά στην πραγματικότητα αντικαθιστά τον χαρακτήρα με μια αποδεκτή αναπαράσταση MySQL για ερωτήματα (π.χ. \n αντικαθίσταται με το "\ n" κυριολεκτικά). (\, ", και " έχουν διαφύγει όπως τεκμηριώνεται) Αυτό δεν αλλάζει τον τρόπο χρήσης αυτής της συνάρτησης, αλλά νομίζω ότι είναι καλό να το γνωρίζετε.

6 χρόνια πριν

Καμία συζήτηση για τη διαφυγή δεν είναι πλήρης χωρίς να πείτε σε όλους ότι βασικά δεν πρέπει ποτέ να χρησιμοποιείτε εξωτερική είσοδο για τη δημιουργία ερμηνευμένου κώδικα. Αυτό ισχύει για εντολές SQL ή οτιδήποτε θα ονομάζατε οποιοδήποτε είδος συνάρτησης "eval".

Έτσι, αντί να χρησιμοποιήσετε αυτήν την τρομερά σπασμένη συνάρτηση, χρησιμοποιήστε παραμετρικές προετοιμασμένες εντολές.

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

Τι σημαίνει αυτό?

Αυτό σημαίνει ότι αντί να δημιουργείτε μια δήλωση SQL όπως αυτή:

"INSERT INTO X (A) VALUES(".$_POST["a"].)"

Θα πρέπει να χρησιμοποιήσετε τη συνάρτηση προετοιμασίας() της mysqli () για να εκτελέσετε μια πρόταση που μοιάζει με αυτό:

"INSERT INTO X (A) Value(?)"

Σημείωση: Αυτό δεν σημαίνει ότι δεν πρέπει ποτέ να δημιουργείτε δυναμικές δηλώσεις SQL. Αυτό σημαίνει ότι δεν πρέπει ποτέ να χρησιμοποιείτε δεδομένα που παρέχονται από το χρήστη για τη δημιουργία αυτών των δηλώσεων. Οποιαδήποτε δεδομένα που παρέχονται από τον χρήστη θα πρέπει να διαβιβάζονται ως παράμετροι στη δήλωση αφού ολοκληρωθούν προετοιμάστηκε.

Έτσι, για παράδειγμα, εάν δημιουργείτε ένα μικρό πλαίσιο και θέλετε να κάνετε μια εισαγωγή σε έναν πίνακα με βάση το URI αιτήματος, είναι προς το συμφέρον σας να μην λάβετε την τιμή $_SERVER["REQUEST_URI"] (ή οποιαδήποτε άλλη μέρος του) και να το συνδέσετε απευθείας με το ερώτημά σας. Αντίθετα, θα πρέπει να αναλύσετε το τμήμα της τιμής $_SERVER["REQUEST_URI"] που θέλετε και να το αντιστοιχίσετε μέσω κάποιου είδους συνάρτησης ή συσχετιστικού πίνακα σε έναν μη χρήστη Εάν η αντιστοίχιση δεν παράγει τιμή, γνωρίζετε ότι κάτι δεν πάει καλά με τα δεδομένα που παρείχε ο χρήστης.

Η αποτυχία τήρησης αυτού ήταν η αιτία πολλών προβλημάτων έγχυσης SQL στο πλαίσιο Ruby On Rails, παρόλο που χρησιμοποιεί παραμετρικές προετοιμασμένες δηλώσεις. Αυτός ήταν ο τρόπος με τον οποίο το GitHub χάκαραν κάποια στιγμή. Έτσι, καμία γλώσσα δεν είναι απρόσβλητη σε αυτό το πρόβλημα. Γι' αυτό είναι μια γενική βέλτιστη πρακτική και όχι κάτι συγκεκριμένο για την PHP και γιατί πρέπει ΠΡΑΓΜΑΤΙΚΑ να την υιοθετήσετε.

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

πριν 9 χρόνια

Υπάρχει μια ενδιαφέρουσα ιδιορρυθμία στο παράδειγμα #2 σχετικά με την έγχυση SQL: Το AND έχει προτεραιότητα έναντι του OR, επομένως το ερώτημα που εισήχθη στην πραγματικότητα εκτελείται ως WHERE (user="aidan" AND password="") Ή ""="", οπότε αντί αυτού της επιστροφής μιας εγγραφής βάσης δεδομένων που αντιστοιχεί σε ένα αυθαίρετο όνομα χρήστη (σε αυτήν την περίπτωση "aidan"), θα επέστρεφε στην πραγματικότητα ΟΛΕΣ τις εγγραφές της βάσης δεδομένων. Χωρίς συγκεκριμένη σειρά. Έτσι, ένας εισβολέας μπορεί να είναι σε θέση να συνδεθεί ως οποιοσδήποτε λογαριασμός, αλλά όχι απαραίτητα με οποιονδήποτε έλεγχος για το ποιος λογαριασμός είναι.

Φυσικά ένας δυνητικός επίθεσης θα μπορούσε απλώς να τροποποιήσει τις παραμέτρους του για να στοχεύσει συγκεκριμένους χρήστες που ενδιαφέρουν:

//Π.χ. αξίες του επιτιθέμενου
$_POST [ "username" ] = "" ;
$_POST["password"] = "" Ή χρήστης = "διαχειριστής" ΚΑΙ "" = "";

// Εσφαλμένη μορφή ερωτήματος
$ερώτημα = "SELECT * FROM users WHERE user="$_POST [ όνομα χρήστη ] " AND password=" $_POST [ κωδικός ] "" ;

echo $query ;

// Το ερώτημα που αποστέλλεται στη MySQL θα είναι:
// SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
// που θα επέτρεπε σε οποιονδήποτε να αποκτήσει πρόσβαση στον λογαριασμό με το όνομα "διαχειριστής"

?>

Πριν 1 χρόνο

@feedr
Το σημείωμά του το ανέπτυξα ως εξής:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$array1 = array("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$array2 = πίνακας("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
echo($string);
echo(PHP_EOL);
για($i=0; $i εάν ($i==0)
$p = "/(?αλλού
$p = "/(?echo($i);
echo($p);
echo($array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo ("\t");
echo($string);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);

Πριν 2 χρόνια

Για να αναφέρω τον Sam στο Numb Safari

[ "Καμία συζήτηση για τη διαφυγή δεν ολοκληρώνεται χωρίς να πείτε σε όλους ότι βασικά δεν πρέπει να χρησιμοποιείτε ποτέ εξωτερική είσοδο για τη δημιουργία ερμηνευμένου κώδικα. Αυτό ισχύει για δηλώσεις SQL ή οτιδήποτε θα ονομάζατε οποιαδήποτε συνάρτηση "eval".

Έτσι, αντί να χρησιμοποιήσετε αυτήν την τρομερά σπασμένη συνάρτηση, χρησιμοποιήστε παραμετρικές προετοιμασμένες εντολές.

Ειλικρινά, η χρήση δεδομένων που παρέχονται από τους χρήστες για τη σύνταξη δηλώσεων SQL θα πρέπει να θεωρείται επαγγελματική αμέλεια και θα πρέπει να λογοδοτείτε από τον εργοδότη ή τον πελάτη σας για τη μη χρήση παραμετρικών προετοιμασμένων δηλώσεων." ]

Έχει δίκιο ο Σαμ......

Ωστόσο, δεν νομίζω ότι είναι λογικό να σταματήσουμε κάθε εξυγίανση και απλώς να αναθέσουμε το έργο σε παραμετρικά προετοιμασμένες δηλώσεις.

Ένας συγκεκριμένος προγραμματιστής που εργάζεται σε μια συγκεκριμένη κατάσταση θα γνωρίζει πάντα περισσότερα σχετικά με την έγκυρη εισαγωγή (συγκεκριμένα σε αυτό το πλαίσιο).

Εάν ζητήσετε από έναν χρήστη να μεταβιβάσει μια τιμή που του έχετε ήδη δώσει και γνωρίζετε ότι όλες αυτές οι τιμές ξεκινούν AB****** και η συμβολοσειρά πρέπει να είναι μήκους 7 ή 11 αλλά ποτέ άλλου μήκους, τότε έχετε η βάση ενός καλού προ-απολυμαντικού - διαφορετικά επιτρεπόμενα μήκη μιας συμβολοσειράς μπορεί να υποδηλώνουν δεδομένα παλαιού τύπου.

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

Με αυτόν τον τρόπο το DB μου δεν φράσσεται με μη ασφαλείς δηλώσεις που γίνονται ασφαλείς - απλά δεν φράσσεται που είναι καλύτερο.

Ασφάλεια στα επίπεδα - η απολύμανση και η επικύρωση θα πρέπει να λαμβάνονται υπόψη σε κάθε περίπτωση ΠΡΙΝ τη χρήση προετοιμασμένων δηλώσεων.

Επιπλέον, όσο μπορώ να διαβάσω το επίσημο έγγραφο
==============================================

"Escaping και SQL injection

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

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

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

Εφόσον η απολύμανση εκτελείται σωστά χωρίς να υποστούν πρόσθετους κινδύνους, τότε προσωπικά θα επιμείνω σε ορισμένα επίπεδα απολύμανσης και στη συνέχεια θα καλέσω τις προετοιμασμένες δηλώσεις.


Πρώτον, λίγο για το γιατί χρειάζονται αυτές οι κάθετες γενικά.
Εάν αντικαταστήσουμε οποιαδήποτε δεδομένα σε ένα ερώτημα, τότε για να διακρίνουμε αυτά τα δεδομένα από τις εντολές SQL, πρέπει να τοποθετηθούν σε εισαγωγικά.
Για παράδειγμα, αν γράφεις
ΕΠΙΛΟΓΗ * ΑΠΟ τον πίνακα ΟΠΟΥ όνομα = Λογαριασμός
τότε η βάση δεδομένων θα αποφασίσει ότι το Bill είναι το όνομα ενός άλλου πεδίου, δεν θα το βρει και θα στείλει ένα σφάλμα. Επομένως, τα δεδομένα που έχουν αντικατασταθεί (σε αυτήν την περίπτωση, το όνομα Bill) πρέπει να περικλείονται σε εισαγωγικά - τότε η βάση δεδομένων θα τα θεωρήσει ως συμβολοσειρά, η τιμή της οποίας πρέπει να εκχωρηθεί στο πεδίο ονόματος:
ΕΠΙΛΟΓΗ * ΑΠΟ τον πίνακα WHERE όνομα = "Λογαριασμός"
Ωστόσο, τα εισαγωγικά μπορεί επίσης να εμφανίζονται στα ίδια τα δεδομένα. Π.χ,
ΕΠΙΛΟΓΗ * ΑΠΟ Πίνακα WHERE όνομα = "D"Artagnan"
Εδώ η βάση δεδομένων θα αποφασίσει ότι το "D" είναι δεδομένα, και το Artagnan είναι μια εντολή που δεν γνωρίζει και θα ρίξει επίσης ένα σφάλμα. Επομένως, είναι απαραίτητο να ανιχνευθούν όλα τα δεδομένα για να εξηγηθεί στη βάση δεδομένων ότι τα εισαγωγικά (και κάποιοι άλλοι ειδικοί χαρακτήρες) που βρίσκονται σε αυτά αναφέρονται στα δεδομένα.
Ως αποτέλεσμα, θα λάβουμε ένα σωστό αίτημα που δεν θα προκαλέσει σφάλματα:
ΕΠΙΛΟΓΗ * ΑΠΟ τον πίνακα WHERE name = "D\"Artagnan"

Έτσι, ανακαλύψαμε ότι κατά την αντικατάσταση δεδομένων συμβολοσειράς σε ένα ερώτημα, πρέπει να ακολουθούνται δύο κανόνες:
- όλα τα δεδομένα συμβολοσειράς που έχουν εισαχθεί πρέπει να περικλείονται σε εισαγωγικά (μονά ή διπλά, αλλά τα μεμονωμένα είναι πιο βολικά και χρησιμοποιούνται πιο συχνά).
- Οι ειδικοί χαρακτήρες πρέπει να διαφεύγουν με κάθετες.

Πρέπει να σημειωθεί ιδιαίτερα: οι πρόσθετες κάθετες ΔΕΝ μπαίνουν στη βάση δεδομένων. Χρειάζονται μόνο στο αίτημα. Όταν χτυπάτε τη βάση, οι κάθετες απορρίπτονται. Κατά συνέπεια, ένα συνηθισμένο λάθος είναι η χρήση stripslash κατά την ανάκτηση δεδομένων από τη βάση δεδομένων.

Όλα τα παραπάνω ισχύουν για δεδομένα συμβολοσειρών και ημερομηνίες. Οι αριθμοί μπορούν να εισαχθούν χωρίς να ακολουθήσουν ή να τους περιβάλουν με εισαγωγικά. Αν το κάνεις αυτό τότε ΑΝΑΓΚΑΙΩΣ!πιέστε τα δεδομένα στον επιθυμητό τύπο πριν τα εισαγάγετε στο ερώτημα, για παράδειγμα:
$id = intval ($id);
Ωστόσο, για απλότητα (και αξιοπιστία), μπορείτε να εργαστείτε με αριθμούς όπως και με συμβολοσειρές (καθώς η mysql εξακολουθεί να τους μετατρέπει στον επιθυμητό τύπο). Αντίστοιχα, θα ανιχνεύσουμε τυχόν δεδομένα που εισάγονται στο αίτημα και θα τα περικλείουμε σε εισαγωγικά.

Επίσης, υπάρχει ένας ακόμη κανόνας - προαιρετικός, αλλά θα πρέπει να τηρηθεί για να αποφευχθούν σφάλματα:
Τα ονόματα των πεδίων και των πινάκων πρέπει να περικλείονται σε πίσω μονά εισαγωγικά - "`" (το πλήκτρο με αυτό το σύμβολο βρίσκεται σε ένα τυπικό πληκτρολόγιο στα αριστερά του πλήκτρου "1"). Άλλωστε, το όνομα του πεδίου μπορεί να συμπίπτει με το mysql λέξεις-κλειδιά, αλλά αν χρησιμοποιήσουμε ένα πίσω απόσπασμα, τότε η MySQL θα καταλάβει ότι όλα είναι σωστά:
SELECT * FROM `table` WHERE `date` = "2006-04-04"
Θα πρέπει να κάνετε διάκριση μεταξύ αυτών των εισαγωγικών και να μην συγχέετε το ένα με το άλλο. Θα πρέπει επίσης να θυμάστε ότι τα backtick δεν ξεφεύγουν από κάθετο.

Έτσι, μάθαμε πώς να αντικαθιστούμε σωστά τα δεδομένα σε ένα αίτημα.
ΑΛΛΑ! Η δυναμική κατασκευή ερωτημάτων δεν περιορίζεται στην αντικατάσταση δεδομένων. Συχνά πρέπει να αντικαταστήσουμε εντολές SQL και ονόματα πεδίων σε ένα ερώτημα. Και εδώ περνάμε στο θέμα της ασφάλειας:

Το SQL Injection είναι μια μέθοδος επίθεσης χάκερ όταν τα δεδομένα που μεταφέρονται σε ένα σενάριο τροποποιούνται με τέτοιο τρόπο ώστε το ερώτημα που δημιουργείται σε αυτό το σενάριο αρχίζει να εκτελεί κάτι εντελώς διαφορετικό από αυτό για το οποίο προοριζόταν.
Οι κανόνες για την προστασία από τέτοιες επιθέσεις μπορούν να χωριστούν σε δύο σημεία:
1. Εργασία με δεδομένα.
2. Εργασία με στοιχεία ελέγχου ερωτήματος.

Συζητήσαμε το πρώτο σημείο λεπτομερώς παραπάνω. Μπορούμε να πούμε ότι στην πραγματικότητα δεν είναι άμυνα. Η συμμόρφωση με τους κανόνες για την προσθήκη δεδομένων σε ένα ερώτημα υπαγορεύεται, πρώτα απ 'όλα, από τις απαιτήσεις του SQL SYNTAX. Και ως παρενέργεια, έχουμε επίσης προστασία από το hacking.

Το δεύτερο σημείο είναι πολύ πιο δύσκολο, δεδομένου ότι δεν υπάρχει ενιαίος καθολικός κανόνας όσον αφορά τα δεδομένα - ένα backtick δεν θα προστατεύσει το όνομα του πεδίου από την τροποποίηση από έναν χάκερ. Δεν είναι δυνατή η χρήση εισαγωγικών για την προστασία ονομάτων πινάκων, δηλώσεων SQL, παραμέτρων εντολών LIMIT και άλλων δηλώσεων.
Επομένως, ο βασικός κανόνας κατά την αντικατάσταση στοιχείων ελέγχου σε ένα ερώτημα είναι:
Εάν χρειάζεται να εισαγάγετε δυναμικά εντολές SQL ή ονόματα πεδίων, βάσεων δεδομένων, πινάκων σε ένα ερώτημα, τότε σε καμία περίπτωση δεν πρέπει να τα εισάγετε απευθείας στο ερώτημα.
Όλες οι επιλογές για τέτοιες προσθήκες πρέπει να είναι γραμμένες εκ των προτέρων στο σενάριό σας και να επιλέγονται με βάση αυτό που εισήγαγε ο χρήστης.
Για παράδειγμα, εάν πρέπει να μεταβιβάσετε ένα όνομα πεδίου στην παραγγελία ανά χειριστή, τότε σε καμία περίπτωση δεν πρέπει να το αντικαταστήσετε απευθείας. Πρώτα πρέπει να το ελέγξουμε. Για παράδειγμα, δημιουργήστε έναν πίνακα έγκυρων τιμών και αντικαταστήστε τον στο αίτημα μόνο εάν η παράμετρος που περάσατε υπάρχει σε αυτόν τον πίνακα:
$orders =array("name" , "price" , "qty" );
$key = array_search($_GET["sort"], $orders));
$orderby = $παραγγελίες [ $key ];
$ερώτημα = "ΕΠΙΛΟΓΗ * ΑΠΟ "Πίνακας" ORDER BY$orderby " ;

Πραγματοποιούμε αναζήτηση στον πίνακα των προκαθορισμένων επιλογών για τη λέξη που έχει εισάγει ο χρήστης και αν τη βρούμε, επιλέγουμε το αντίστοιχο στοιχείο του πίνακα. Εάν δεν βρεθεί αντιστοιχία, θα επιλεγεί το πρώτο στοιχείο του πίνακα.
Έτσι, αυτό που αντικαθίσταται στο αίτημα δεν είναι αυτό που εισήγαγε ο χρήστης, αλλά αυτό που γράφτηκε στο σενάριό μας.
Το ίδιο πρέπει να γίνει και σε όλες τις άλλες περιπτώσεις.
Για παράδειγμα, εάν η ρήτρα WHERE δημιουργείται δυναμικά:
if (!empty($_GET [ "price" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$query = " SELECT * FROM `table` WHERE $where " ;

Είναι δύσκολο για μένα να φανταστώ μια περίπτωση όπου ένα όνομα πίνακα μπορεί να εισαχθεί σε ένα ερώτημα δυναμικά, αλλά αν συμβεί αυτό, τότε το όνομα πρέπει επίσης να εισαχθεί μόνο από ένα σύνολο προκαθορισμένο στο σενάριο.
Οι παράμετροι του τελεστή LIMIT θα πρέπει να εξαναγκαστούν σε έναν ακέραιο τύπο χρησιμοποιώντας αριθμητικές πράξεις ή τη συνάρτηση intval().
Μην νομίζετε ότι τα παραδείγματα που αναφέρονται εδώ εξαντλούν όλες τις επιλογές για δυναμική κατασκευή ερωτημάτων. Απλά πρέπει να κατανοήσετε την αρχή και να την εφαρμόσετε σε όλες αυτές τις περιπτώσεις.

Λόγω της φύσης της εργασίας μου, πρέπει να πραγματοποιήσω ελέγχους ασφαλείας του πηγαίο κώδικα των εφαρμογών Ιστού.
Πολλές διαδικτυακές εφαρμογές και πολύς κώδικας...

Δεν είναι μυστικό ότι οι ευπάθειες SQL injection είναι οι πιο κοινές από όλες τις ευπάθειες εφαρμογών ιστού από την πλευρά του διακομιστή. Υπάρχουν πλατφόρμες και πλαίσια όπου τέτοια πράγματα αποκλείονται σχεδόν εντελώς, για παράδειγμα ORM και ούτω καθεξής. Αλλά τα στατιστικά στοιχεία μας λένε επίμονα για την απόλυτη κυριαρχία των εφαρμογών ιστού με απλά συνενωμένα ερωτήματα SQL στο Διαδίκτυο. Επιπλέον, υπάρχουν περιπτώσεις όπου το ORM είναι γενικά εφαρμόσιμο Δεν μπορεί, για παράδειγμα, όταν όχι μόνο οι παράμετροι των παραστάσεων, αλλά και η ίδια η λογική του ερωτήματος σε επίπεδο χειριστή πρέπει να εξαρτώνται από τα δεδομένα χρήστη.

Λοιπόν, ας ξεκινήσουμε.

Άχρηστη απόδραση χαρακτήρα
Βρέθηκε στο 83% των διαδικτυακών εφαρμογών PHP που είναι ευάλωτες σε ενέσεις SQL
Χρήση της λειτουργίας διαφυγής για χαρακτήρες όπως
mysql_escape_string
mysql_real_escape_string
πρόσθετες κάθετες
χωρίς εισαγωγικά. Τις περισσότερες φορές εκδηλώνεται σε αριθμητικές παραμέτρους (όλα τα είδη *_id).
Παράδειγμα
$sql = "ΕΠΙΛΟΓΗ χρήστη ΑΠΟ λίστα χρηστών WHERE userid=".mysql_real_escape_string($_GET["uid"]);

Φαίνεται να είναι ασφαλής κωδικός, αλλά μόνο στην επιφάνεια. Το πιο κοινό μοτίβο ενέσεων SQL στην PHP στο ιατρείο μου μπήκε εδώ. Για να επιτεθεί σε αυτήν την ευπάθεια, ένας εισβολέας πρέπει απλώς να αποφύγει τη χρήση των χαρακτήρων \x00 \r \n \x1a στο διάνυσμα επίθεσης.
Για παράδειγμα:
/index.php?uid=-777 UNION SELECT κωδικό πρόσβασης από τη λίστα χρηστών

Αναζήτηση στον κώδικα
Περιπλέκεται από τη σημασιολογία της γλώσσας. Για μια απλή αναζήτηση μπορείτε να χρησιμοποιήσετε το egrep:
egrep -Rin "(επιλογή|ενημέρωση|εισαγωγή|διαγραφή|αντικατάσταση).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|προσθαφαιρέσεις)" . | grep -v "[\""]["\"]"

Η λογική της έκφρασης αναζήτησης είναι η εξής: βρείτε όλες τις γραμμές στις οποίες δεν υπάρχει ακολουθία χαρακτήρων εισαγωγικών ("", "", "", "") στα αριστερά των συναρτήσεων φιλτραρίσματος. Η μέθοδος, φυσικά, απέχει πολύ από το 100%, αλλά είναι αδύνατο να απαιτηθεί μια κανονική έκφραση για την εκτέλεση σημασιολογικής ανάλυσης.
Για να διευκολύνετε την εμφάνιση πληροφοριών, μπορείτε να επισημάνετε τη λειτουργία έγχρωμα στην κονσόλα:
egrep -Rin "(επιλογή|ενημέρωση|εισαγωγή|διαγραφή|αντικατάσταση).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|προσθαφαιρέσεις)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

Για να προστατευτείτε από αυτήν την ευπάθεια χαρακτήρων μπαλαντέρ, είναι καλύτερο να χρησιμοποιήσετε τη χύτευση τύπου.
Αυτό λειτουργεί πάντα πιο γρήγορα και είναι πιο αξιόπιστο από όλα τα είδη φιλτραρίσματος και ελέγχου.
Για το παραπάνω παράδειγμα, η ενημερωμένη έκδοση κώδικα θα μπορούσε να είναι ως εξής:
$sql = "ΕΠΙΛΟΓΗ χρήστη ΑΠΟ λίστα χρηστών WHERE userid=".intval($_GET["uid"]);

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

Οπότε βασικά έσκαψα βαθιά τους τομείς της MySQL και της PHP... συγκεκριμένα τα μέτρα ασφαλείας που πρέπει να λάβω όταν ασχολούμαι με τις εισόδους της βάσης δεδομένων και των φορμών. Μέχρι στιγμής έχω βρει τα εξής να μου προτείνονται ανεπιφύλακτα:

  1. Έτοιμες δηλώσεις
  2. Χρησιμοποιώντας _real_escape_string()
  3. ΔΕΝ χρησιμοποιεί μαγικά εισαγωγικά καθώς μπερδεύει τις βάσεις δεδομένων και καταλήγει να σας δίνει πράγματα όπως "Δεν το κάλεσες...".

Όλα αυτά είναι υπέροχα και το παρακολουθώ. Ωστόσο, αναρωτιόμουν αν έπρεπε να ξεφύγω από χαρακτήρες όπως το σύμβολο του δολαρίου [$], το σύμβολο τοις εκατό [%] και ίσως άλλους. Θα μπορούσε το ερώτημα να ερμηνεύσει το σύμβολο του δολαρίου ως μεταβλητή PHP; Τι γίνεται με τη σύνταξη LIKE που έχω ακούσει ότι χρησιμοποιεί το σύμβολο % ή ακόμα και μπαλαντέρ; Οι προετοιμασμένες δηλώσεις θα έπρεπε τεχνικά να φροντίζουν για όλα αυτά, αλλά ήθελα απλώς να είμαι στην ασφαλή πλευρά και να βεβαιωθώ ότι κάλυψα τα πάντα σωστά. Σε περιπτώσεις που ξεχνάω να χρησιμοποιήσω προετοιμασμένες δηλώσεις ή απλώς τις παραμελώ, ήλπιζα ότι αυτή η δεύτερη γραμμή άμυνας θα μπορούσε να μου πει ότι θα μπορούσα να απαλλαγώ από τη ζάλη.

Εδώ είναι τι χρησιμοποιώ αυτήν τη στιγμή για απόδραση:

Συνάρτηση escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecials ($new_data, ENT_NOQUOTES); επιστροφή $new_data; )

Είναι λοιπόν σωστό αυτό; Κάνω κάτι τρομερά λάθος; Λάβετε υπόψη ότι κατά την επιστροφή των δεδομένων της βάσης δεδομένων θα πρέπει να αφαιρέσω τις ανάστροφες κάθετες πριν από τους χαρακτήρες $,% και _.

Κάνω κάτι τρομερά λάθος;

Πρώτα για την έρευνά σας.

Έτοιμες δηλώσεις – ο μοναδικόςυπέροχο πράγμα που βρήκες.

Αν και η χρήση mysqli_real_escape_string (υποθέτοντας ότι χρησιμοποιείτε προετοιμασμένες δηλώσεις) θα ήταν άχρηστο και επιβλαβές(δημιουργώντας ένα αποτέλεσμα που σημειώσατε μόνοι σας: «Τηλεφώνησες δεν είναι...»).

Και τα Magic Quotes έχουν αφαιρεθεί εδώ και καιρό από τη γλώσσα - επομένως δεν αξίζουν τίποτα.

Έτσι, ακόμη και οι περισσότερες από τις αρχικές σας εγκαταστάσεις είναι σαφώς λάθος.

Τώρα στην ερώτησή σου.

Θα μπορούσε το ερώτημα να ερμηνεύσει το σύμβολο του δολαρίου ως μεταβλητή PHP;

Τι γίνεται με τη σύνταξη LIKE που έχω ακούσει ότι χρησιμοποιεί το σύμβολο % ή ακόμα και μπαλαντέρ;

Ναι, καλά ακούσατε. Ο ακριβής σκοπός του τελεστή LIKE είναι να πραγματοποιήσει αναζήτηση μοτίβων. Η απενεργοποίηση αυτών των χαρακτήρων στο LIKE δεν θα είχε το παραμικρό νόημα.

Κάθε φορά που πρόκειται να χρησιμοποιήσετε τον τελεστή LIKE, πρέπει να αποφασίσετε ποιο συγκεκριμένο χαρακτήρα να χρησιμοποιήσετε και ποιον να μην επιτρέψετε. Δεν μπορείτε να χρησιμοποιήσετε μια εφάπαξ λύση. Για να μην αναφέρουμε ότι σε όλες τις άλλες αλληλεπιδράσεις mysql το σύμβολο % δεν έχει ιδιαίτερη σημασία.

Οι προετοιμασμένες δηλώσεις θα πρέπει τεχνικά να φροντίζουν για όλα αυτά

Οι προετοιμασμένες δηλώσεις δεν έχουν καμία σχέση με τα σύμβολα $ ή %. Οι προετοιμασμένες δηλώσεις αναφέρονται στην ένεση SQL, αλλά κανένας χαρακτήρας δεν μπορεί να την προκαλέσει (δεν θα μπορούσατε να αποκαλέσετε την "ένεση" έναν σωστά χρησιμοποιημένο τελεστή LIKE, έτσι δεν είναι;).

Τέλος, στο χειρότερο μέρος.

Σε περίπτωση που ξεχάσετε να χρησιμοποιήσετε έτοιμες δηλώσεις ή απλώς αμελήσετε να τις ακολουθήσετε,

τίποτα δεν θα σε σώσει.

Και η ελάχιστη βοήθεια θα ήταν από τη λειτουργία που αναπτύξατε.

Συνοψίζω.

  1. Απαλλαγείτε από αυτό το χαρακτηριστικό.
  2. Χρήση πληρωτικά *για την αναπαράσταση κάθε μεμονωμένης μεταβλητής στο ερώτημα.
  3. Escape % και _ χαρακτήρες στην είσοδο μόνο εάν θα χρησιμοποιηθούν στον τελεστή LIKE και δεν θέλετε να ερμηνευτούν.
  4. Χρησιμοποιήστε htmlspecialchars() για έξοδο, όχι είσοδο mysql.

*Διαβάστε έτοιμες δηλώσεις εάν αυτός ο όρος δεν σας είναι οικείος.

Δεν χρειάζεται να αποφύγετε το σύμβολο του δολαρίου. Η MySQL δεν εξετάζει αυτόν τον χαρακτήρα συγκεκριμένα και η PHP τον αναγνωρίζει μόνο στον πηγαίο κώδικα, όχι σε τιμές συμβολοσειρών (εκτός αν καλέσετε το eval στη συμβολοσειρά, αλλά αυτό είναι ένα εντελώς άλλο σκουλήκι σκουληκιών).

Θα χρειαζόταν μόνο να ξεφύγετε από το % και το _ εάν χρησιμοποιούσατε την εισαγωγή χρήστη ως όρισμα LIKE και δεν θέλετε ο χρήστης να μπορεί να χρησιμοποιεί χαρακτήρες μπαλαντέρ. Αυτό μπορεί να συμβεί εάν επεξεργάζεστε μια φόρμα αναζήτησης. Δεν χρειάζεται να το χρησιμοποιείτε κατά την αποθήκευση σε μια βάση δεδομένων.

Δεν χρειάζεται να χρησιμοποιείτε htmlspecialchars κατά την πρόσβαση στη βάση δεδομένων. Αυτό θα πρέπει να χρησιμοποιείται μόνο κατά την εμφάνιση δεδομένων στο χρήστη σε μια σελίδα HTML για την αποφυγή της εισαγωγής XSS.

Ανάλογα με ποια δεδομένα και σε τι χρησιμοποιείται.

Εάν διαπιστώσετε ότι οι προεπιλεγμένες out-of-the-box δηλώσεις της PHP είναι πολύ μεγάλες και πολύπλοκες, προτείνω να ρίξετε μια ματιά σε μερικές από τις τάξεις που είναι διαθέσιμες στο github για να σας δώσουμε μια ιδέα για τα απλοποιημένα ερωτήματα.

Ένα παράδειγμα εισαγωγής ερωτημάτων με αυτήν την κλάση

$data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("secretpassword+salt")), // password = SHA1("createdAt" => $db->now(), // createAt = NOW() " expires" => $db->now("+1Y") // expires = NOW() + διάστημα 1 έτος // Υποστηριζόμενα διαστήματα [s]second, [m]minute, [h]hour, [d]day, [Μήνας χρόνος); $id = $db->insert("users", $data); if ($id) echo "user δημιουργήθηκε. Id=" . $id; else echo "η εισαγωγή απέτυχε: " . $db->getLastError();