Το Latte είναι συνώνυμο της ασφάλειας
Το Latte είναι το μοναδικό σύστημα templating για PHP με αποτελεσματική προστασία έναντι της κρίσιμης ευπάθειας Cross-site Scripting (XSS). Και αυτό χάρη στο λεγόμενο context-aware escaping. Θα μιλήσουμε για,
- ποια είναι η αρχή της ευπάθειας XSS και γιατί είναι τόσο επικίνδυνη
- γιατί το Latte είναι τόσο αποτελεσματικό στην άμυνα έναντι του XSS
- πώς μπορεί κανείς εύκολα να δημιουργήσει ένα κενό ασφαλείας στα templates Twig, Blade κ.λπ.
Cross-site Scripting (XSS)
Το Cross-site Scripting (συντομογραφία XSS) είναι μια από τις πιο συχνές ευπάθειες των ιστοσελίδων και ταυτόχρονα πολύ επικίνδυνη. Επιτρέπει σε έναν εισβολέα να εισάγει ένα κακόβουλο script (το λεγόμενο malware) σε μια ξένη σελίδα, το οποίο εκτελείται στον browser ενός ανυποψίαστου χρήστη.
Τι μπορεί να κάνει ένα τέτοιο script; Μπορεί, για παράδειγμα, να στείλει στον εισβολέα οποιοδήποτε περιεχόμενο από την παραβιασμένη σελίδα, συμπεριλαμβανομένων ευαίσθητων δεδομένων που εμφανίζονται μετά τη σύνδεση. Μπορεί να τροποποιήσει τη σελίδα ή να εκτελέσει περαιτέρω αιτήματα για λογαριασμό του χρήστη. Αν, για παράδειγμα, ήταν ένα webmail, θα μπορούσε να διαβάσει ευαίσθητα μηνύματα, να τροποποιήσει το εμφανιζόμενο περιεχόμενο ή να αλλάξει τις ρυθμίσεις, π.χ. να ενεργοποιήσει την προώθηση αντιγράφων όλων των μηνυμάτων στη διεύθυνση του εισβολέα, ώστε να αποκτήσει πρόσβαση και σε μελλοντικά emails.
Γι' αυτό το XSS φιγουράρει στις πρώτες θέσεις των κατατάξεων των πιο επικίνδυνων ευπαθειών. Εάν εμφανιστεί μια ευπάθεια σε μια ιστοσελίδα, πρέπει να αφαιρεθεί το συντομότερο δυνατό για να αποφευχθεί η εκμετάλλευση.
Πώς προκύπτει η ευπάθεια;
Το σφάλμα προκύπτει στο σημείο όπου δημιουργείται η ιστοσελίδα και εκτυπώνονται οι μεταβλητές. Φανταστείτε ότι δημιουργείτε μια σελίδα με αναζήτηση και στην αρχή θα υπάρχει μια παράγραφος με τον όρο αναζήτησης με τη μορφή:
echo '<p>Αποτελέσματα αναζήτησης για <em>' . $search . '</em></p>';
Ένας εισβολέας μπορεί να γράψει στο πεδίο αναζήτησης και κατ'
επέκταση στη μεταβλητή $search
οποιοδήποτε string, δηλαδή και κώδικα
HTML όπως <script>alert("Hacked!")</script>
. Επειδή η έξοδος δεν
επεξεργάζεται με κανέναν τρόπο, γίνεται μέρος της εμφανιζόμενης
σελίδας:
<p>Αποτελέσματα αναζήτησης για <em><script>alert("Hacked!")</script></em></p>
Ο browser, αντί να εκτυπώσει το string αναζήτησης, εκτελεί το JavaScript. Και έτσι ο εισβολέας παίρνει τον έλεγχο της σελίδας.
Μπορείτε να αντιτάξετε ότι με την εισαγωγή κώδικα στη μεταβλητή, το JavaScript εκτελείται, αλλά μόνο στον browser του εισβολέα. Πώς φτάνει στο θύμα; Από αυτή την άποψη, διακρίνουμε διάφορους τύπους XSS. Στο παράδειγμά μας με την αναζήτηση, μιλάμε για reflected XSS. Εδώ, είναι ακόμα απαραίτητο να καθοδηγήσετε το θύμα να κάνει κλικ σε έναν σύνδεσμο που θα περιέχει κακόβουλο κώδικα στην παράμετρο:
https://example.com/?search=<script>alert("Hacked!")</script>
Η καθοδήγηση του χρήστη στον σύνδεσμο απαιτεί μεν κάποια κοινωνική
μηχανική, αλλά δεν είναι κάτι περίπλοκο. Οι χρήστες κάνουν κλικ σε
συνδέσμους, είτε σε emails είτε στα κοινωνικά δίκτυα, χωρίς πολλή σκέψη.
Και το ότι υπάρχει κάτι ύποπτο στη διεύθυνση μπορεί να καλυφθεί
χρησιμοποιώντας έναν συντομευτή URL, ο χρήστης βλέπει τότε μόνο
bit.ly/xxx
.
Ωστόσο, υπάρχει και μια δεύτερη και πολύ πιο επικίνδυνη μορφή επίθεσης που ονομάζεται stored XSS ή persistent XSS, όπου ο εισβολέας καταφέρνει να αποθηκεύσει τον κακόβουλο κώδικα στον server έτσι ώστε να εισάγεται αυτόματα σε ορισμένες σελίδες.
Ένα παράδειγμα είναι οι σελίδες όπου οι χρήστες γράφουν σχόλια. Ο εισβολέας στέλνει μια δημοσίευση που περιέχει κώδικα και αυτός αποθηκεύεται στον server. Εάν οι σελίδες δεν είναι επαρκώς ασφαλισμένες, θα εκτελείται στη συνέχεια στον browser κάθε επισκέπτη.
Θα μπορούσε να φανεί ότι ο πυρήνας της επίθεσης έγκειται στο να
εισαχθεί στη σελίδα το string <script>
. Στην πραγματικότητα, οι τρόποι εισαγωγής
JavaScript είναι πολλοί. Ας δείξουμε ένα παράδειγμα εισαγωγής μέσω ενός
attribute HTML. Ας έχουμε μια συλλογή φωτογραφιών όπου μπορούν να εισαχθούν
λεζάντες στις εικόνες, οι οποίες εκτυπώνονται στο attribute alt
:
echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">';
Αρκεί ο εισβολέας να εισάγει ως λεζάντα ένα έξυπνα κατασκευασμένο
string " onload="alert('Hacked!')
και αν η εκτύπωση δεν επεξεργαστεί, ο
προκύπτων κώδικας θα μοιάζει ως εξής:
<img src="photo0145.webp" alt="" onload="alert('Hacked!')">
Μέρος της σελίδας γίνεται τώρα το πλαστό attribute onload
. Ο browser
εκτελεί τον κώδικα που περιέχεται σε αυτό αμέσως μετά τη λήψη της
εικόνας. Hacked!
Πώς να αμυνθείτε από το XSS;
Οποιεσδήποτε προσπάθειες ανίχνευσης επιθέσεων μέσω blacklist, όπως για
παράδειγμα ο αποκλεισμός του string <script>
κ.λπ., είναι
ανεπαρκείς. Η βάση μιας λειτουργικής άμυνας είναι η συνεπής
απολύμανση όλων των δεδομένων που εκτυπώνονται μέσα στη σελίδα.
Κυρίως, πρόκειται για την αντικατάσταση όλων των χαρακτήρων με
ειδική σημασία με άλλες αντίστοιχες ακολουθίες, κάτι που στην αργκό
ονομάζεται escaping (ο πρώτος χαρακτήρας της ακολουθίας ονομάζεται
χαρακτήρας διαφυγής, από όπου και η ονομασία). Για παράδειγμα, στο
κείμενο HTML, ο χαρακτήρας <
έχει ειδική σημασία, ο οποίος, όταν
δεν πρέπει να ερμηνευθεί ως αρχή ενός tag, πρέπει να αντικατασταθεί με
μια οπτικά αντίστοιχη ακολουθία, τη λεγόμενη οντότητα HTML <
.
Και ο browser εκτυπώνει το σύμβολο μικρότερο.
Είναι πολύ σημαντικό να διακρίνουμε το context στο οποίο εκτυπώνουμε τα δεδομένα. Επειδή σε διαφορετικά contexts, τα strings απολυμαίνονται διαφορετικά. Σε διαφορετικά contexts, διαφορετικοί χαρακτήρες έχουν ειδική σημασία. Για παράδειγμα, το escaping διαφέρει στο κείμενο HTML, στα attributes HTML, μέσα σε ορισμένα ειδικά στοιχεία, κ.λπ. Θα το συζητήσουμε λεπτομερώς σε λίγο.
Η επεξεργασία είναι καλύτερο να γίνεται απευθείας κατά την εκτύπωση του string στη σελίδα, εξασφαλίζοντας έτσι ότι όντως πραγματοποιείται και πραγματοποιείται ακριβώς μία φορά. Το καλύτερο είναι αν η επεξεργασία γίνεται αυτόματα απευθείας από το σύστημα templating. Επειδή αν η επεξεργασία δεν γίνεται αυτόματα, ο προγραμματιστής μπορεί να την ξεχάσει. Και μία παράλειψη σημαίνει ότι ο ιστότοπος είναι ευάλωτος.
Ωστόσο, το XSS δεν αφορά μόνο την εκτύπωση δεδομένων στα templates, αλλά και
άλλα μέρη της εφαρμογής που πρέπει να χειρίζονται σωστά τα μη
αξιόπιστα δεδομένα. Για παράδειγμα, είναι απαραίτητο το JavaScript στην
εφαρμογή σας να μην χρησιμοποιεί innerHTML
σε σχέση με αυτά, αλλά
μόνο innerText
ή textContent
. Ιδιαίτερη προσοχή πρέπει να δοθεί
στις συναρτήσεις που αξιολογούν strings ως JavaScript, όπως η eval()
, αλλά
και η setTimeout()
, ή η χρήση της συνάρτησης setAttribute()
με event
attributes όπως onload
κ.λπ. Αυτό όμως ξεφεύγει από την περιοχή που
καλύπτουν τα templates.
Η ιδανική άμυνα σε 3 σημεία:
- αναγνωρίζει το context στο οποίο εκτυπώνονται τα δεδομένα
- απολυμαίνει τα δεδομένα σύμφωνα με τους κανόνες του δεδομένου context (δηλαδή “context-aware”)
- το κάνει αυτόματα
Context-Aware Escaping
Τι ακριβώς εννοούμε με τη λέξη context; Πρόκειται για ένα μέρος στο έγγραφο με τους δικούς του κανόνες για την επεξεργασία των εκτυπωμένων δεδομένων. Εξαρτάται από τον τύπο του εγγράφου (HTML, XML, CSS, JavaScript, plain text, …) και μπορεί να διαφέρει σε συγκεκριμένα μέρη του. Για παράδειγμα, σε ένα έγγραφο HTML, υπάρχουν πολλά τέτοια μέρη (contexts) όπου ισχύουν πολύ διαφορετικοί κανόνες. Ίσως εκπλαγείτε πόσα είναι. Εδώ έχουμε την πρώτη τετράδα:
<p>#text</p>
<img src="#atribut">
<textarea>#rawtext</textarea>
<!-- #komentář -->
Το προεπιλεγμένο και βασικό context μιας σελίδας HTML είναι το κείμενο HTML.
Ποιοι κανόνες ισχύουν εδώ; Οι χαρακτήρες <
και &
έχουν
ειδική σημασία, καθώς αντιπροσωπεύουν την αρχή ενός tag ή μιας
οντότητας, οπότε πρέπει να τους κάνουμε escape, αντικαθιστώντας τους με
την οντότητα HTML (<
με <
, &
με &
).
Το δεύτερο πιο συνηθισμένο context είναι η τιμή ενός attribute HTML. Διαφέρει
από το κείμενο στο ότι εδώ ειδική σημασία έχουν τα εισαγωγικά "
ή
'
, τα οποία περικλείουν το attribute. Αυτά πρέπει να γραφτούν ως
οντότητα, ώστε να μην εκληφθούν ως το τέλος του attribute. Αντίθετα, στο
attribute μπορεί να χρησιμοποιηθεί με ασφάλεια ο χαρακτήρας <
,
επειδή εδώ δεν έχει καμία ειδική σημασία, εδώ δεν μπορεί να εκληφθεί ως
αρχή ενός tag ή σχολίου. Αλλά προσοχή, στο HTML μπορούν να γραφτούν τιμές
attributes και χωρίς εισαγωγικά, σε αυτή την περίπτωση μια ολόκληρη σειρά
χαρακτήρων έχει ειδική σημασία, πρόκειται λοιπόν για ένα άλλο
ξεχωριστό context.
Ίσως σας εκπλήξει, αλλά ειδικοί κανόνες ισχύουν μέσα στα στοιχεία
<textarea>
και <title>
, όπου ο χαρακτήρας <
δεν
χρειάζεται (αλλά μπορεί) να γίνει escape, αν δεν ακολουθείται από /
.
Αλλά αυτό είναι μάλλον μια λεπτομέρεια.
Ενδιαφέρον παρουσιάζει το εσωτερικό των σχολίων HTML. Εδώ, το escaping δεν γίνεται με οντότητες HTML. Μάλιστα, καμία προδιαγραφή δεν αναφέρει πώς πρέπει να γίνεται το escaping στα σχόλια. Απλά πρέπει να τηρούνται κάπως περίεργοι κανόνες και να αποφεύγονται σε αυτά ορισμένοι συνδυασμοί χαρακτήρων.
Τα contexts μπορούν επίσης να στρωματοποιηθούν, κάτι που συμβαίνει όταν ενσωματώνουμε JavaScript ή CSS σε HTML. Αυτό μπορεί να γίνει με δύο διαφορετικούς τρόπους, με στοιχείο και με attribute:
<script>#js-element</script>
<img onclick="#js-atribut">
<style>#css-element</style>
<p style="#css-atribut"></p>
Δύο δρόμοι και δύο διαφορετικοί τρόποι escaping δεδομένων. Μέσα στα
στοιχεία <script>
και <style>
, όπως και στην περίπτωση των
σχολίων HTML, το escaping με οντότητες HTML δεν πραγματοποιείται. Κατά την
εκτύπωση δεδομένων μέσα σε αυτά τα στοιχεία, πρέπει να τηρηθεί ένας
μόνο κανόνας: το κείμενο δεν πρέπει να περιέχει την ακολουθία
</script
αντίστοιχα </style
.
Αντίθετα, στα attributes style
και on***
, το escaping γίνεται με
οντότητες HTML.
Και φυσικά, μέσα στο ενσωματωμένο JavaScript ή CSS ισχύουν οι κανόνες escaping
αυτών των γλωσσών. Έτσι, ένα string σε ένα attribute, π.χ. onload
, γίνεται
πρώτα escape σύμφωνα με τους κανόνες της JS και στη συνέχεια σύμφωνα με
τους κανόνες του attribute HTML.
Ουφ… Όπως βλέπετε, το HTML είναι ένα πολύ πολύπλοκο έγγραφο όπου τα contexts στρωματοποιούνται, και χωρίς να συνειδητοποιήσουμε πού ακριβώς εκτυπώνουμε τα δεδομένα (δηλαδή σε ποιο context), δεν μπορούμε να πούμε πώς να το κάνουμε σωστά.
Θέλετε ένα παράδειγμα;
Ας έχουμε το string Rock'n'Roll
.
Αν το εκτυπώσετε σε κείμενο HTML, σε αυτή τη συγκεκριμένη περίπτωση δεν χρειάζεται να κάνετε καμία αντικατάσταση, επειδή το string δεν περιέχει κανέναν χαρακτήρα με ειδική σημασία. Η κατάσταση αλλάζει αν το εκτυπώσετε μέσα σε ένα attribute HTML που περικλείεται σε απλά εισαγωγικά. Σε αυτή την περίπτωση, πρέπει να κάνετε escape τα εισαγωγικά σε οντότητες HTML:
<div title='Rock'n'Roll'></div>
Αυτό ήταν απλό. Μια πολύ πιο ενδιαφέρουσα κατάσταση προκύπτει κατά τη στρωματοποίηση των contexts, για παράδειγμα, αν το string είναι μέρος ενός JavaScript.
Αρχικά, λοιπόν, το εκτυπώνουμε στο ίδιο το JavaScript. Δηλαδή, το
περικλείουμε σε εισαγωγικά και ταυτόχρονα κάνουμε escape με τον
χαρακτήρα \
τα εισαγωγικά που περιέχονται σε αυτό:
'Rock\'n\'Roll'
Μπορούμε ακόμα να προσθέσουμε την κλήση κάποιας συνάρτησης, ώστε ο κώδικας να κάνει κάτι:
alert('Rock\'n\'Roll');
Αν εισάγουμε αυτόν τον κώδικα σε ένα έγγραφο HTML χρησιμοποιώντας το
<script>
, δεν χρειάζεται να τροποποιήσουμε τίποτα άλλο, επειδή
δεν περιέχει την απαγορευμένη ακολουθία </script
:
<script> alert('Rock\'n\'Roll'); </script>
Αν όμως θέλαμε να το εισάγουμε σε ένα attribute HTML, πρέπει ακόμα να κάνουμε escape τα εισαγωγικά σε οντότητες HTML:
<div onclick='alert('Rock\'n\'Roll')'></div>
Ωστόσο, το ενσωματωμένο context δεν χρειάζεται να είναι μόνο JS ή CSS. Συχνά
είναι επίσης ένα URL. Οι παράμετροι σε ένα URL γίνονται escape μετατρέποντας
τους χαρακτήρες με ειδική σημασία σε ακολουθίες που ξεκινούν με
%
. Παράδειγμα:
https://example.org/?a=Jazz&b=Rock%27n%27Roll
Και όταν εκτυπώνουμε αυτό το string σε ένα attribute, εφαρμόζουμε επιπλέον το
escaping σύμφωνα με αυτό το context και αντικαθιστούμε το &
με
&
:
<a href="https://example.org/?a=Jazz&b=Rock%27n%27Roll">
Αν διαβάσατε μέχρι εδώ, συγχαρητήρια, ήταν εξαντλητικό. Τώρα έχετε μια καλή ιδέα για το τι είναι τα contexts και το escaping. Και δεν χρειάζεται να ανησυχείτε ότι είναι περίπλοκο. Το Latte το κάνει αυτό για εσάς αυτόματα.
Latte εναντίον απλοϊκών συστημάτων
Δείξαμε πώς γίνεται σωστά το escaping σε ένα έγγραφο HTML και πόσο θεμελιώδης είναι η γνώση του context, δηλαδή του μέρους όπου εκτυπώνουμε τα δεδομένα. Με άλλα λόγια, πώς λειτουργεί το context-aware escaping. Αν και πρόκειται για απαραίτητη προϋπόθεση για μια λειτουργική άμυνα έναντι του XSS, το Latte είναι το μοναδικό σύστημα templating για PHP που το κάνει αυτό.
Πώς είναι δυνατόν, αφού όλα τα συστήματα σήμερα ισχυρίζονται ότι έχουν αυτόματο escaping; Το αυτόματο escaping χωρίς γνώση του context είναι λίγο μπούρδα, που δημιουργεί μια ψευδή αίσθηση ασφάλειας.
Συστήματα templating, όπως το Twig, το Laravel Blade και άλλα, δεν βλέπουν καμία δομή HTML στο template. Επομένως, δεν βλέπουν ούτε τα contexts. Σε σύγκριση με το Latte, είναι τυφλά και απλοϊκά. Επεξεργάζονται μόνο τα δικά τους tags, όλα τα άλλα είναι για αυτά μια ασήμαντη ροή χαρακτήρων:
░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░
░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░{{ foo }}░░░░
- σε κείμενο: <span>{{ foo }}</span>
- σε tag: <span {{ foo }} ></span>
- σε attribute: <span title='{{ foo }}'></span>
- σε attribute χωρίς εισαγωγικά: <span title={{ foo }}></span>
- σε attribute που περιέχει URL: <a href="{{ foo }}"></a>
- σε attribute που περιέχει JavaScript: <img onload="{{ foo }}">
- σε attribute που περιέχει CSS: <span style="{{ foo }}"></span>
- σε JavaScript: <script>var = {{ foo }}</script>
- σε CSS: <style>body { content: {{ foo }}; }</style>
- σε σχόλιο: <!-- {{ foo }} -->
Τα απλοϊκά συστήματα απλώς μετατρέπουν μηχανικά τους χαρακτήρες
< > & ' "
σε οντότητες HTML, κάτι που, αν και στις περισσότερες
περιπτώσεις χρήσης είναι ένας έγκυρος τρόπος escaping, δεν ισχύει πάντα.
Δεν μπορούν έτσι να αποκαλύψουν ούτε να αποτρέψουν τη δημιουργία
διαφόρων κενών ασφαλείας, όπως θα δείξουμε παρακάτω.
Το Latte βλέπει το template όπως εσείς. Καταλαβαίνει HTML, XML, αναγνωρίζει tags, attributes κ.λπ. Και χάρη σε αυτό, διακρίνει τα επιμέρους contexts και επεξεργάζεται τα δεδομένα ανάλογα. Προσφέρει έτσι πραγματικά αποτελεσματική προστασία έναντι της κρίσιμης ευπάθειας Cross-site Scripting.
░░░░░░░░░░░<span>{$foo}</span>
░░░░░░░░░░<span {$foo} ></span>
░░░░░░░░░░░░░░<span title='{$foo}'></span>
░░░░░░░░░░░░░░░░░░░░░░░░░░░<span title={$foo}></span>
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░<a href="{$foo}"></a>
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░<img onload="{$foo}">
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░<span style="{$foo}"></span>
░░░░░░░░░░░░░░░░░<script>░░░░░░{$foo}</script>
░░░░░░░░░<style>░░░░░░░░░░░░░░░░{$foo}░░░</style>
░░░░░░░░░░░░░░░<!--░{$foo}░-->
- σε κείμενο: <span>{$foo}</span>
- σε tag: <span {$foo} ></span>
- σε attribute: <span title='{$foo}'></span>
- σε attribute χωρίς εισαγωγικά: <span title={$foo}></span>
- σε attribute που περιέχει URL: <a href="{$foo}"></a>
- σε attribute που περιέχει JavaScript: <img onload="{$foo}">
- σε attribute που περιέχει CSS: <span style="{$foo}"></span>
- σε JavaScript: <script>var = {$foo}</script>
- σε CSS: <style>body { content: {$foo}; }</style>
- σε σχόλιο: <!-- {$foo} -->
Ζωντανό παράδειγμα
Αριστερά βλέπετε το template στο Latte, δεξιά είναι ο παραγόμενος κώδικας
HTML. Η μεταβλητή $text
εκτυπώνεται αρκετές φορές εδώ και κάθε φορά
σε ένα ελαφρώς διαφορετικό context. Και επομένως, γίνεται και ελαφρώς
διαφορετικά escape. Μπορείτε να επεξεργαστείτε μόνοι σας τον κώδικα του
template, για παράδειγμα, να αλλάξετε το περιεχόμενο της μεταβλητής κ.λπ.
Δοκιμάστε το:
Δεν είναι υπέροχο! Το Latte κάνει το context-aware escaping αυτόματα, οπότε ο προγραμματιστής:
- δεν χρειάζεται να σκέφτεται ούτε να ξέρει πώς γίνεται το escape πού
- δεν μπορεί να κάνει λάθος
- δεν μπορεί να ξεχάσει το escaping
Αυτά δεν είναι καν όλα τα contexts που διακρίνει το Latte κατά την εκτύπωση και για τα οποία προσαρμόζει την επεξεργασία των δεδομένων. Θα εξετάσουμε τώρα άλλες ενδιαφέρουσες περιπτώσεις.
Πώς να χακάρετε απλοϊκά συστήματα
Σε μερικά πρακτικά παραδείγματα, θα δείξουμε πόσο σημαντική είναι η διάκριση των contexts και γιατί τα απλοϊκά συστήματα templating δεν παρέχουν επαρκή προστασία από το XSS, σε αντίθεση με το Latte. Ως εκπρόσωπο ενός απλοϊκού συστήματος, θα χρησιμοποιήσουμε το Twig στα παραδείγματα, αλλά το ίδιο ισχύει και για άλλα συστήματα.
Ευπάθεια μέσω attribute
Θα προσπαθήσουμε να εισάγουμε κακόβουλο κώδικα στη σελίδα μέσω ενός attribute HTML, όπως δείξαμε παραπάνω. Ας έχουμε ένα template στο Twig που αποδίδει μια εικόνα:
<img src={{ imageFile }} alt={{ imageAlt }}>
Παρατηρήστε ότι γύρω από τις τιμές των attributes δεν υπάρχουν εισαγωγικά. Ο κωδικοποιητής μπορεί να τα ξέχασε, κάτι που απλά συμβαίνει. Για παράδειγμα, στο React ο κώδικας γράφεται έτσι, χωρίς εισαγωγικά, και ένας κωδικοποιητής που αλλάζει γλώσσες μπορεί εύκολα να ξεχάσει τα εισαγωγικά.
Ένας εισβολέας εισάγει ως λεζάντα της εικόνας ένα έξυπνα
κατασκευασμένο string foo onload=alert('Hacked!')
. Γνωρίζουμε ήδη ότι το Twig δεν
μπορεί να αναγνωρίσει αν η μεταβλητή εκτυπώνεται στη ροή του κειμένου
HTML, μέσα σε ένα attribute, σε ένα σχόλιο HTML κ.λπ., με λίγα λόγια δεν διακρίνει
τα contexts. Και απλώς μετατρέπει μηχανικά τους χαρακτήρες
< > & ' "
σε οντότητες HTML. Έτσι, ο προκύπτων κώδικας θα μοιάζει
ως εξής:
<img src=photo0145.webp alt=foo onload=alert('Hacked!')>
Και δημιουργήθηκε ένα κενό ασφαλείας!
Μέρος της σελίδας έγινε το πλαστό attribute onload
και ο browser το
εκτελεί αμέσως μετά τη λήψη της εικόνας.
Τώρα ας δούμε πώς το Latte χειρίζεται το ίδιο template:
<img src={$imageFile} alt={$imageAlt}>
Το Latte βλέπει το template όπως εσείς. Σε αντίθεση με το Twig, καταλαβαίνει HTML και ξέρει ότι η μεταβλητή εκτυπώνεται ως τιμή ενός attribute που δεν βρίσκεται σε εισαγωγικά. Γι' αυτό τα συμπληρώνει. Όταν ο εισβολέας εισάγει την ίδια λεζάντα, ο προκύπτων κώδικας θα μοιάζει ως εξής:
<img src="photo0145.webp" alt="foo onload=alert('Hacked!')">
Το Latte απέτρεψε με επιτυχία το XSS.
Εκτύπωση μεταβλητής σε JavaScript
Χάρη στο context-aware escaping, είναι εξαιρετικά εύκολο να χρησιμοποιείτε μεταβλητές PHP μέσα σε JavaScript.
<p onclick="alert({$movie})">{$movie}</p>
<script>var movie = {$movie};</script>
Εάν η μεταβλητή $movie
περιέχει το string 'Amarcord & 8 1/2'
, θα
παραχθεί η ακόλουθη έξοδος. Παρατηρήστε ότι μέσα στο HTML
χρησιμοποιείται διαφορετικό escaping από ό,τι μέσα στο JavaScript και ακόμα
διαφορετικό στο attribute onclick
:
<p onclick="alert("Amarcord & 8 1\/2")">Amarcord & 8 1/2</p>
<script>var movie = "Amarcord & 8 1\/2";</script>
Έλεγχος συνδέσμων
Το Latte ελέγχει αυτόματα εάν η μεταβλητή που χρησιμοποιείται στα attributes
src
ή href
περιέχει μια διεύθυνση URL ιστού (δηλαδή πρωτόκολλο
HTTP) και αποτρέπει την εμφάνιση συνδέσμων που ενδέχεται να αποτελούν
κίνδυνο για την ασφάλεια.
{var $link = 'javascript:attack()'}
<a href={$link}>κάντε κλικ</a>
Εκτυπώνει:
<a href="">κάντε κλικ</a>
Ο έλεγχος μπορεί να απενεργοποιηθεί χρησιμοποιώντας το φίλτρο nocheck.
Όρια του Latte
Το Latte δεν αποτελεί εντελώς πλήρη προστασία από το XSS για ολόκληρη την εφαρμογή. Δεν θα θέλαμε να σταματήσετε να σκέφτεστε την ασφάλεια όταν χρησιμοποιείτε το Latte. Ο στόχος του Latte είναι να διασφαλίσει ότι ένας εισβολέας δεν μπορεί να τροποποιήσει τη δομή της σελίδας, να πλαστογραφήσει στοιχεία ή attributes HTML. Αλλά δεν ελέγχει την ορθότητα του περιεχομένου των εκτυπωμένων δεδομένων. Ούτε την ορθότητα της συμπεριφοράς του JavaScript. Αυτό ξεπερνά τις αρμοδιότητες ενός συστήματος templating. Η επαλήθευση της ορθότητας των δεδομένων, ειδικά αυτών που εισάγονται από τον χρήστη και επομένως είναι μη αξιόπιστα, είναι ένα σημαντικό καθήκον του προγραμματιστή.