Τι είναι το SQL Injection και πώς να το αποτρέψετε σε εφαρμογές PHP;

Άρα πιστεύετε ότι η βάση δεδομένων SQL είναι αποτελεσματική και ασφαλής από άμεση καταστροφή; Λοιπόν, η SQL Injection διαφωνεί!

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

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

Τι είναι το SQL Injection;

Το κλειδί για την κατανόηση του SQL Injection βρίσκεται στο όνομά του: SQL + Injection. Η λέξη “ένεση” εδώ δεν έχει καμία ιατρική σημασία, αλλά μάλλον είναι η χρήση του ρήματος “ένεση”. Μαζί, αυτές οι δύο λέξεις μεταφέρουν την ιδέα της τοποθέτησης της SQL σε μια εφαρμογή Ιστού.

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

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

<form action="record_message.php" method="POST">
  <label>Your name</label>
  <input type="text" name="name">
  
  <label>Your message</label>
  <textarea name="message" rows="5"></textarea>
  
  <input type="submit" value="Send">
</form>

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

<?php

$name = $_POST['name'];
$message = $_POST['message'];

// check if this user already has a message
mysqli_query($conn, "SELECT * from messages where name = $name");

// Other code here

Επομένως, προσπαθείτε πρώτα να δείτε εάν αυτός ο χρήστης έχει ήδη ένα μη αναγνωσμένο μήνυμα. Το ερώτημα SELECT * από μηνύματα όπου name = $name φαίνεται αρκετά απλό, σωστά;

ΛΑΝΘΑΣΜΕΝΟΣ!

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

  • Η εφαρμογή εκτελείται σε βάση δεδομένων SQL (σήμερα, σχεδόν κάθε εφαρμογή είναι)
  • Η τρέχουσα σύνδεση βάσης δεδομένων έχει δικαιώματα “επεξεργασίας” και “διαγραφής” στη βάση δεδομένων
  • Μπορείτε να μαντέψετε τα ονόματα των σημαντικών πινάκων
  Πώς να αφαιρέσετε τον εαυτό σας από το Διαδίκτυο

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

Joe? περικοπή παραγγελιών; Μάλιστα κύριε! Ας δούμε τι θα γίνει το ερώτημα όταν εκτελεστεί από το σενάριο PHP:

ΕΠΙΛΟΓΗ * ΑΠΟ μηνύματα WHERE όνομα = Joe; περικοπή παραγγελιών?

Εντάξει, το πρώτο μέρος του ερωτήματος έχει ένα συντακτικό σφάλμα (χωρίς εισαγωγικά γύρω από το “Joe”), αλλά το ερωτηματικό αναγκάζει τη μηχανή MySQL να αρχίσει να ερμηνεύει ένα νέο: περικοπή παραγγελιών. Ακριβώς έτσι, με ένα μόνο swoop, ολόκληρο το ιστορικό παραγγελιών έχει χαθεί!

Τώρα που ξέρετε πώς λειτουργεί το SQL Injection, ήρθε η ώρα να εξετάσετε πώς να το σταματήσετε. Οι δύο προϋποθέσεις που πρέπει να πληρούνται για την επιτυχή ένεση SQL είναι:

  • Το σενάριο PHP θα πρέπει να έχει δικαιώματα τροποποίησης/διαγραφής στη βάση δεδομένων. Νομίζω ότι αυτό ισχύει για όλες τις εφαρμογές και δεν θα μπορείτε να κάνετε τις εφαρμογές σας μόνο για ανάγνωση. 🙂 Και μαντέψτε, ακόμα κι αν καταργήσουμε όλα τα δικαιώματα τροποποίησης, η ένεση SQL μπορεί να επιτρέψει σε κάποιον να εκτελέσει ερωτήματα SELECT και να προβάλει όλη τη βάση δεδομένων, συμπεριλαμβανομένων των ευαίσθητων δεδομένων. Με άλλα λόγια, η μείωση του επιπέδου πρόσβασης στη βάση δεδομένων δεν λειτουργεί και η εφαρμογή σας το χρειάζεται ούτως ή άλλως.
  • Η εισαγωγή του χρήστη υποβάλλεται σε επεξεργασία. Ο μόνος τρόπος με τον οποίο μπορεί να λειτουργήσει η ένεση SQL είναι όταν δέχεστε δεδομένα από χρήστες. Για άλλη μια φορά, δεν είναι πρακτικό να σταματήσετε όλες τις εισόδους για την εφαρμογή σας μόνο και μόνο επειδή ανησυχείτε για την ένεση SQL.
  • Πρόληψη της ένεσης SQL στην PHP

    Τώρα, δεδομένου ότι οι συνδέσεις βάσεων δεδομένων, τα ερωτήματα και οι εισροές χρηστών είναι μέρος της ζωής, πώς μπορούμε να αποτρέψουμε την ένεση SQL; Ευτυχώς, είναι αρκετά απλό και υπάρχουν δύο τρόποι για να το κάνετε: 1) απολύμανση των εισροών χρήστη και 2) χρήση προετοιμασμένων δηλώσεων.

    Εξυγίανση της εισόδου χρήστη

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

    Για παράδειγμα, εάν έχετε μια συμβολοσειρά όπως το I’m a string, ο χαρακτήρας ενός εισαγωγικού (‘) μπορεί να χρησιμοποιηθεί από έναν εισβολέα για να χειριστεί το ερώτημα της βάσης δεδομένων που δημιουργείται και να προκαλέσει μια ένεση SQL. Εκτελώντας το μέσω της mysql_real_escape_string() δημιουργείται το I’m a string, το οποίο προσθέτει μια ανάστροφη κάθετο στο μεμονωμένο εισαγωγικό, διαφεύγοντας από αυτό. Ως αποτέλεσμα, ολόκληρη η συμβολοσειρά περνά πλέον ως αβλαβής συμβολοσειρά στη βάση δεδομένων, αντί να μπορεί να συμμετέχει στη χειραγώγηση ερωτημάτων.

      11 Καλύτερη διαχείριση WooCommerce Hosting για το ηλεκτρονικό σας κατάστημα

    Υπάρχει ένα μειονέκτημα με αυτήν την προσέγγιση: είναι μια πραγματικά, πολύ παλιά τεχνική που ταιριάζει με τις παλαιότερες μορφές πρόσβασης σε βάσεις δεδομένων στην PHP. Από την PHP 7, αυτή η συνάρτηση δεν υπάρχει καν, κάτι που μας φέρνει στην επόμενη λύση μας.

    Χρησιμοποιήστε προετοιμασμένες δηλώσεις

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

    <?php
    $servername = "localhost";
    $username = "username";
    $password = "password";
    $dbname = "myDB";
    
    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }
    
    // prepare and bind
    $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $firstname, $lastname, $email);
    
    // set parameters and execute
    $firstname = "John";
    $lastname = "Doe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Mary";
    $lastname = "Moe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Julie";
    $lastname = "Dooley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "New records created successfully";
    
    $stmt->close();
    $conn->close();
    ?>

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

    Για όσους γνωρίζουν ήδη την επέκταση ΠΟΠ της PHP και τη χρησιμοποιούν για τη δημιουργία προετοιμασμένων δηλώσεων, έχω μια μικρή συμβουλή.

    Προειδοποίηση: Να είστε προσεκτικοί κατά τη ρύθμιση ΠΟΠ

    Όταν χρησιμοποιούμε ΠΟΠ για πρόσβαση στη βάση δεδομένων, μπορεί να παρασυρθούμε σε μια ψευδή αίσθηση ασφάλειας. «Α, καλά, χρησιμοποιώ ΠΟΠ. Τώρα δεν χρειάζεται να σκέφτομαι τίποτα άλλο» — έτσι πάει η σκέψη μας γενικά. Είναι αλήθεια ότι το PDO (ή οι προετοιμασμένες δηλώσεις MySQLi) αρκούν για να αποτρέψουν κάθε είδους επιθέσεις SQL injection, αλλά πρέπει να είστε προσεκτικοί όταν το ρυθμίζετε. Είναι σύνηθες να κάνετε απλώς αντιγραφή-επικόλληση κώδικα από σεμινάρια ή από προηγούμενα έργα σας και να προχωρήσετε, αλλά αυτή η ρύθμιση μπορεί να αναιρέσει τα πάντα:

    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

    Αυτό που κάνει αυτή η ρύθμιση είναι να λέει στο PDO να μιμηθεί προετοιμασμένες δηλώσεις αντί να χρησιμοποιήσει πραγματικά τη δυνατότητα προετοιμασμένων δηλώσεων της βάσης δεδομένων. Κατά συνέπεια, η PHP στέλνει απλές συμβολοσειρές ερωτημάτων στη βάση δεδομένων, ακόμα κι αν ο κώδικάς σας μοιάζει σαν να δημιουργεί προετοιμασμένες δηλώσεις και να ορίζει παραμέτρους και όλα αυτά. Με άλλα λόγια, είστε τόσο ευάλωτοι στην έγχυση SQL όπως πριν. 🙂

      Πώς να χρησιμοποιήσετε το FaceTime Handoff σε iPhone, iPad και Mac

    Η λύση είναι απλή: βεβαιωθείτε ότι αυτή η εξομοίωση έχει οριστεί σε false.

    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

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

    Πρόληψη με χρήση WAF

    Γνωρίζετε ότι μπορείτε επίσης να προστατεύσετε εφαρμογές Ιστού από την έγχυση SQL χρησιμοποιώντας το WAF (τείχος προστασίας εφαρμογών ιστού);

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

    SQL injection και σύγχρονα πλαίσια PHP

    Η ένεση SQL είναι τόσο συνηθισμένη, τόσο εύκολη, τόσο απογοητευτική και τόσο επικίνδυνη που όλα τα σύγχρονα πλαίσια ιστού PHP είναι ενσωματωμένα με αντίμετρα. Στο WordPress, για παράδειγμα, έχουμε τη συνάρτηση $wpdb->prepare(), ενώ αν χρησιμοποιείτε ένα πλαίσιο MVC, κάνει όλη τη βρώμικη δουλειά για εσάς και δεν χρειάζεται καν να σκεφτείτε να αποτρέψετε την ένεση SQL. Είναι λίγο ενοχλητικό το γεγονός ότι στο WordPress πρέπει να προετοιμάζετε δηλώσεις ρητά, αλλά hey, μιλάμε για το WordPress. 🙂

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

    συμπέρασμα

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