Έλεγχος εγκυρότητας, sanitization και πρόληψη επιθέσεων injection

Μάθε πώς ο έλεγχος εγκυρότητας και το sanitization προστατεύουν τις φόρμες από επιθέσεις injection και κάνουν τα δεδομένα ασφαλή.

Πρόχειρο

Όταν επιτίθενται σε φόρμες, οι επιτιθέμενοι δεν τις συμπληρώνουν κανονικά. Στέλνουν κώδικα που προσπαθεί να μπει στα συστήματά σου ή να κλέψει δεδομένα. Μπορεί να στείλουν JavaScript που θα τρέξει στον browser των επισκεπτών, εντολές SQL που χαλούν τη βάση δεδομένων ή κακόβουλο περιεχόμενο. Ο έλεγχος εγκυρότητας και το sanitization είναι η άμυνα που σταματά αυτές τις επιθέσεις.

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

Τι είναι ο έλεγχος εγκυρότητας;

Ο έλεγχος εγκυρότητας (validation) ελέγχει αν τα δεδομένα που στέλνονται στη φόρμα σου έχουν τη μορφή που περιμένεις και τηρούν τους κανόνες σου. Είναι η πρώτη γραμμή άμυνας απέναντι σε κακόβουλα δεδομένα.

Ο έλεγχος απαντά σε ερωτήσεις όπως: είναι αυτό διεύθυνση email ή τυχαίο κείμενο; Είναι αριθμός ή λέξη; Είναι κενό το πεδίο ενώ δεν πρέπει; Έγραψε ο επισκέπτης κάτι που έχει νόημα για το πεδίο ή κάτι ύποπτο;

Παραδείγματα ελέγχων:

  • Ένα πεδίο email πρέπει να έχει το σύμβολο @ και ένα domain
  • Ένα πεδίο τηλεφώνου πρέπει να έχει μόνο ψηφία και παύλες
  • Ένα πεδίο αριθμού δεν πρέπει να έχει γράμματα
  • Ένα υποχρεωτικό πεδίο δεν πρέπει να είναι κενό
  • Ένα πεδίο κωδικού πρέπει να έχει ελάχιστο μήκος

Ο έλεγχος γίνεται σε δύο σημεία. Στη συσκευή του επισκέπτη λέγεται έλεγχος client-side. Στον server σου λέγεται έλεγχος server-side. Ο client-side δίνει άμεση ειδοποίηση στον επισκέπτη όταν κάνει λάθος. Ο server-side είναι αυτός που προστατεύει πραγματικά από επιθέσεις, γιατί οι επιτιθέμενοι μπορούν να παρακάμψουν τους ελέγχους client-side.

Τι είναι το sanitization;

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

Παραδείγματα του τι κάνει το sanitization:

  • Αφαιρεί ετικέτες HTML όπως `<script>` και `<img>` από τα πεδία κειμένου
  • Αφαιρεί ειδικούς χαρακτήρες SQL που θα μπορούσαν να χρησιμοποιηθούν για να παραβιαστεί μια βάση δεδομένων
  • Κάνει escape τους ειδικούς χαρακτήρες, ώστε να αντιμετωπίζονται ως κείμενο και όχι ως κώδικας
  • Περιορίζει το μήκος των δεδομένων, για να αποφύγεις επιθέσεις buffer overflow

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

Πώς δουλεύουν οι επιθέσεις injection

SQL injection

Μια επίθεση SQL injection γίνεται όταν ο επιτιθέμενος βάζει κώδικα SQL σε ένα πεδίο φόρμας. Αν ο κώδικας της φόρμας σου δεν κάνει sanitization σε αυτά τα δεδομένα, ο κώδικας SQL εκτελείται στη βάση δεδομένων σου.

Παράδειγμα: μια φόρμα σύνδεσης ζητά διεύθυνση email. Αντί για το email του, ο επιτιθέμενος στέλνει:

' OR '1'='1

Αν η φόρμα σου δεν κάνει sanitization, ο κώδικάς σου μπορεί να τρέξει ένα ερώτημα SQL που μοιάζει έτσι:

SELECT * FROM users WHERE email = '' OR '1'='1'

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

Cross-Site Scripting (XSS)

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

Παράδειγμα: μια φόρμα σχολίων δέχεται σχόλια χρηστών. Ο επιτιθέμενος στέλνει:

<script>alert('hacked')</script>

Αν αυτό δεν περάσει από sanitization και αποθηκευτεί στη βάση δεδομένων, όποιος ανοίξει την ενότητα σχολίων θα δει να τρέχει η JavaScript και να εμφανίζεται ένα μήνυμα. Μια πιο επικίνδυνη εκδοχή θα μπορούσε να κλέψει cookies, να στείλει τους επισκέπτες σε σελίδα phishing ή να αντλήσει δεδομένα φόρμας.

Command injection

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

Παράδειγμα: ένα πεδίο φόρμας χρησιμοποιείται σε μια εντολή όπως:

ffmpeg -i $filename output.mp4

Αν το `$filename` δεν περάσει από sanitization και ο επιτιθέμενος στείλει `;rm -rf /`, η εντολή γίνεται:

ffmpeg -i ;rm -rf / output.mp4

Η εντολή `rm -rf /` τρέχει και σβήνει αρχεία από το σύστημα. Γι' αυτό οι εντολές συστήματος δεν πρέπει ποτέ να δέχονται δεδομένα χρήστη χωρίς sanitization.

Έλεγχος εγκυρότητας και sanitization ανά τύπο δεδομένων

Διευθύνσεις email

Έλεγχος εγκυρότητας: Έλεγξε ότι το κείμενο ταιριάζει με μοτίβο email (κάτι σαν text@domain.extension). Επιβεβαίωσε ότι έχει μόνο επιτρεπτούς χαρακτήρες.

Sanitization: Αφαίρεσε ετικέτες HTML και ειδικούς χαρακτήρες. Κράτα μόνο γράμματα, αριθμούς, τελείες, παύλες και το σύμβολο @.

Αριθμοί τηλεφώνου

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

Sanitization: Αφαίρεσε κάθε μη αριθμητικό χαρακτήρα εκτός από παύλες και παρενθέσεις. Αφαίρεσε αρχικά μηδενικά ή κενά, αν χρειάζεται.

Πεδία κειμένου (σχόλια, ονόματα, διευθύνσεις)

Έλεγχος εγκυρότητας: Έλεγξε ότι το πεδίο δεν είναι κενό και ότι το μήκος είναι λογικό (όχι εκατομμύρια χαρακτήρες).

Sanitization: Αφαίρεσε ή κάνε escape ετικέτες HTML, JavaScript και ειδικούς χαρακτήρες SQL. Κράτα μόνο απλό κείμενο και βασική στίξη.

Αριθμοί και ποσά

Έλεγχος εγκυρότητας: Έλεγξε ότι η τιμή είναι αριθμός. Για τιμές ή ποσότητες, έλεγξε ότι είναι θετικός αριθμός.

Sanitization: Αφαίρεσε κάθε μη αριθμητικό χαρακτήρα. Αν χρειάζεται, στρογγύλεψε στα σωστά δεκαδικά ψηφία.

Κωδικοί πρόσβασης

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

Sanitization: Μην αφαιρείς και μην αλλάζεις κωδικούς. Πρέπει να αποθηκεύονται ακριβώς όπως είναι (με hash, ποτέ ως απλό κείμενο). Αν αφαιρέσεις χαρακτήρες από έναν κωδικό, ο χρήστης δεν θα μπορεί να συνδεθεί.

Μεταφόρτωση αρχείων

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

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

Πώς να εφαρμόσεις έλεγχο εγκυρότητας και sanitization

Client-side (στον browser)

Χρησιμοποίησε τα χαρακτηριστικά ελέγχου της HTML5, για να δίνεις άμεση ειδοποίηση στους επισκέπτες:

<input type="email" name="email" required> <input type="tel" name="phone" pattern="[0-9]{10}"> <input type="number" name="age" min="0" max="120">

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

Server-side (στον server σου)

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

// Παράδειγμα σε ψευδοκώδικα if (email does not match email pattern) { reject the submission } // Αφαίρεση ετικετών HTML sanitized_comment = remove_html_tags(comment_text) // Escape ειδικών χαρακτήρων για τη βάση δεδομένων safe_name = escape_sql_characters(user_name) // Μόνο τότε αποθήκευσε τα δεδομένα store_in_database(safe_name, sanitized_comment)

Χρησιμοποίησε παραμετροποιημένα ερωτήματα για βάσεις δεδομένων

Η καλύτερη άμυνα απέναντι στο SQL injection είναι τα παραμετροποιημένα ερωτήματα (parameterized queries ή prepared statements). Αντί να βάζεις τα δεδομένα του χρήστη κατευθείαν μέσα στο ερώτημα SQL, χρησιμοποιείς placeholders και περνάς τα δεδομένα ξεχωριστά:

// Κακό (ευάλωτο): query = "SELECT * FROM users WHERE email = '" + email + "'" // Καλό (ασφαλές): query = "SELECT * FROM users WHERE email = ?" execute(query, [email])

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

Χρησιμοποίησε βιβλιοθήκες και frameworks

Μη γράφεις κώδικα sanitization από το μηδέν. Χρησιμοποίησε καθιερωμένες βιβλιοθήκες που φτιάχτηκαν γι' αυτόν τον σκοπό. Στην PHP χρησιμοποίησε τη htmlspecialchars() ή μια βιβλιοθήκη όπως η HTML Purifier. Στην Python χρησιμοποίησε το module `html` ή μια βιβλιοθήκη όπως η bleach. Στο Node.js χρησιμοποίησε το `express-validator` ή κάτι αντίστοιχο. Αυτές οι βιβλιοθήκες έχουν δοκιμαστεί και ενημερώνονται για νέους τύπους επιθέσεων.

Συνηθισμένα λάθη στον έλεγχο εγκυρότητας και στο sanitization

Έλεγχος μόνο στο client-side. Ο έλεγχος client-side είναι για την εμπειρία χρήσης, όχι για την ασφάλεια. Ο επιτιθέμενος μπορεί να τον παρακάμψει. Πρέπει να ελέγχεις και στον server.

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

Εμπιστοσύνη στον τύπο δεδομένων του πεδίου. Αν έχεις πεδίο αριθμού, ο browser στέλνει κείμενο που μοιάζει με αριθμό. Ο server σου πρέπει να επαληθεύσει ότι είναι πράγματι αριθμός πριν τον χρησιμοποιήσει. Μην υποθέτεις ποτέ ότι ο τύπος δεδομένων είναι σωστός.

Να ξεχνάς το sanitization στα δεδομένα που εμφανίζονται. Ακόμα κι αν τα δεδομένα αποθηκεύονται με ασφάλεια, αν τα εμφανίσεις σε σελίδα χωρίς escape της HTML, οι επιτιθέμενοι μπορούν να βάλουν κώδικα που τρέχει στον browser των επισκεπτών όταν δουν αυτά τα δεδομένα.

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

Φόρμες στο WEMASY

Στον Δημιουργό ιστοσελίδων του WEMASY μπορείς να προσθέσεις φόρμα στην ιστοσελίδα σου. Η επιλογή των πεδίων που θα συλλέγεις είναι δική σου.

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

Συχνές ερωτήσεις

Μπορώ να χρησιμοποιήσω WAF (Web Application Firewall) αντί για έλεγχο εγκυρότητας;
Ένα WAF μπορεί να πιάσει ορισμένες επιθέσεις injection πριν φτάσουν στη φόρμα σου, κάτι που βοηθά. Ωστόσο δεν αντικαθιστά τον έλεγχο εγκυρότητας και το sanitization. Ένα WAF είναι δεύτερη γραμμή άμυνας. Ο κώδικας της φόρμας σου πρέπει ούτως ή άλλως να ελέγχει και να καθαρίζει τα δεδομένα. Αν χρησιμοποιείς και WAF και έλεγχο εγκυρότητας, έχεις πολλά επίπεδα προστασίας.
Τι γίνεται αν ένας επισκέπτης χρειάζεται νόμιμα να στείλει ειδικούς χαρακτήρες;
Ειδικοί χαρακτήρες όπως απόστροφοι, παύλες και ampersand είναι ασφαλείς αν κάνεις σωστό sanitization. Για παράδειγμα, κάποιος μπορεί να έχει επώνυμο όπως "O'Brien" ή "Mary-Jane". Μπορείς να τα δεχτείς με ασφάλεια, αν κάνεις σωστό escape στη βάση δεδομένων (με παραμετροποιημένα ερωτήματα) και στην εμφάνιση (με escape HTML). Το κλειδί είναι το σωστό sanitization και όχι ο αποκλεισμός των χαρακτήρων.
Πρέπει να ελέγχω και στο client και στον server;
Ναι. Ο έλεγχος client-side δίνει άμεση ειδοποίηση στους επισκέπτες και βελτιώνει την εμπειρία (καταλαβαίνουν ότι έκαναν λάθος πριν στείλουν). Ο έλεγχος server-side είναι αυτός που προστατεύει πραγματικά από επιθέσεις. Χρησιμοποίησε και τα δύο επίπεδα. Το client-side είναι για την εμπειρία χρήσης. Το server-side είναι για την ασφάλεια.
Ποια είναι η διαφορά ανάμεσα στο escaping και στο sanitization;
Το escaping μετατρέπει τους επικίνδυνους χαρακτήρες, ώστε να αντιμετωπίζονται ως απλό κείμενο (για παράδειγμα, το < γίνεται <). Το sanitization αφαιρεί εντελώς το επικίνδυνο περιεχόμενο (για παράδειγμα, αφαιρεί όλες τις ετικέτες HTML). Το escaping κρατά τα δεδομένα, το sanitization αφαιρεί την απειλή. Για τα περισσότερα δεδομένα φόρμας, το sanitization (πλήρης αφαίρεση ετικετών) είναι πιο ασφαλές από το escaping (μετατροπή τους).
Πρέπει να ανησυχώ για επιθέσεις injection σε φόρμες μόνο για ανάγνωση;
Αν μια φόρμα μόνο εμφανίζει πληροφορίες (δεν στέλνει τίποτα), δεν έχει ευπάθεια injection. Ωστόσο, αν η φόρμα εμφανίζει ποτέ δεδομένα που έστειλαν χρήστες, πρέπει να κάνεις sanitization. Ένας επιτιθέμενος μπορεί να έχει στείλει κακόβουλο κώδικα νωρίτερα και, όταν τον εμφανίσεις, ο κώδικας τρέχει. Κάνε πάντα sanitization σε κάθε δεδομένο που έστειλε χρήστης πριν το εμφανίσεις, ανεξάρτητα από τον τύπο της φόρμας.

Βάλ’ το αμέσως σε εφαρμογή
στη δική σου ιστοσελίδα.

Ξεκίνα δωρεάν με το WEMASY και φτιάξε τη σελίδα σου σε λίγα λεπτά.