Ασφάλεια έξυπνου συμβολαίου
Τελευταία επεξεργασία: @mr_giorgos(opens in a new tab), 14 Ιανουαρίου 2025
Τα έξυπνα συμβόλαια είναι εξαιρετικά ευέλικτα και ικανά να ελέγχουν μεγάλες ποσότητες αξίας και δεδομένων, ενώ παράλληλα εκτελούν αμετάβλητη λογική με βάση τον κώδικα που αναπτύσσεται στο blockchain. Αυτό δημιούργησε ένα ζωντανό οικοσύστημα έμπιστων και αποκεντρωμένων εφαρμογών που προσφέρουν πολλά πλεονεκτήματα σε σχέση με τα παλαιά συστήματα. Ωστόσο, παρέχουν επίσης ευκαιρίες σε επιτιθέμενους που επιδιώκουν να αποκομίσουν κέρδος εκμεταλλευόμενοι τα τρωτά σημεία στα έξυπνα συμβόλαια.
Τα δημόσια blockchain, όπως το Ethereum, περιπλέκουν περαιτέρω το ζήτημα της ασφάλειας των έξυπνων συμβολαίων. Ο αναπτυγμένος κώδικας συμβολαίων συνήθως δεν μπορεί να αλλάξει για να διορθώσει τρωτότητες της ασφάλειας, ενώ τα περιουσιακά στοιχεία που αποσπώνται από έξυπνα συμβόλαια είναι εξαιρετικά δύσκολο να εντοπιστούν και η ανάκτησή τους είναι ως επί το πλείστον αδύνατη λόγω του αμετακλήτου τους.
Αν και οι αριθμοί διαφέρουν, εκτιμάται ότι το συνολικό ποσό αξίας που κλάπηκε ή χάθηκε λόγω ελαττωμάτων ασφαλείας στα έξυπνα συμβόλαια υπερβαίνει άνετα το 1 δισεκατομμύριο δολάρια. Αυτό περιλαμβάνει υψηλού προφίλ περιστατικά, όπως το hack του DAO(opens in a new tab) (3,6 εκατομμύρια ETH κλαπέντα, αξίας άνω του 1 δισεκατομμυρίου δολαρίων σε σημερινές τιμές), το hack του πορτοφολιού πολλαπλών υπογραφών Parity(opens in a new tab) (30 εκατομμύρια δολάρια χάθηκαν από χάκερ) και το πρόβλημα του παγωμένου πορτοφολιού Parity(opens in a new tab) (πάνω από 300 εκατομμύρια ETH κλειδωμένα για πάντα).
Τα προαναφερθέντα ζητήματα καθιστούν επιτακτική την ανάγκη οι προγραμματιστές να καταβάλουν προσπάθειες για την ανάπτυξη ασφαλών, εύρωστων και ανθεκτικών έξυπνων συμβολαίων. Η ασφάλεια των έξυπνων συμβολαίων είναι σοβαρή υπόθεση και κάθε προγραμματιστής θα πρέπει να μάθει γι' αυτή. Αυτός ο οδηγός θα καλύψει ζητήματα ασφάλειας για προγραμματιστές Ethereum και θα εξερευνήσει πόρους για τη βελτίωση της ασφάλειας των έξυπνων συμβολαίων.
Προαπαιτούμενα
Βεβαιωθείτε ότι είστε εξοικειωμένοι με τα βασικά της ανάπτυξης έξυπνων συμβολαίων πριν ασχοληθείτε με ζητήματα ασφάλειας.
Οδηγίες για την κατασκευή ασφαλών έξυπνων συμβολαίων Ethereum
1. Σχεδιάστε κατάλληλους ελέγχους πρόσβασης
Στα έξυπνα συμβόλαια, οι συναρτήσεις που επισημαίνονται ως public
ή external
μπορούν να καλούνται από οποιονδήποτε λογαριασμό εξωτερικού κατόχου (EOA) ή λογαριασμό συμβολαίου. Είναι απαραίτητο να ορίσετε δημόσια ορατότητα για τις συναρτήσεις εάν θέλετε οι άλλοι να αλληλεπιδράσουν με το συμβόλαιό σας. Ωστόσο, οι συναρτήσεις που επισημαίνονται ως private
μπορούν να καλούνται μόνο από συναρτήσεις εντός του έξυπνου συμβολαίου και όχι από εξωτερικούς λογαριασμούς. Η παροχή πρόσβασης σε όλους τους συμμετέχοντες του δικτύου στις συναρτήσεις συμβολαίων μπορεί να προκαλέσει προβλήματα, ειδικά αν σημαίνει ότι ο καθένας μπορεί να εκτελέσει ευαίσθητες λειτουργίες (π.χ. δημιουργία νέων token).
Για την πρόληψη μη εξουσιοδοτημένης χρήσης των συναρτήσεων έξυπνων συμβολαίων, είναι απαραίτητο να εφαρμοστούν ασφαλείς έλεγχοι πρόσβασης. Οι μηχανισμοί ελέγχου πρόσβασης περιορίζουν τη δυνατότητα χρήσης ορισμένων συναρτήσεων σε ένα έξυπνο συμβόλαιο σε εξουσιοδοτημένες οντότητες, όπως λογαριασμούς υπεύθυνους για τη διαχείριση του συμβολαίου. Το μοτίβο Ownable και ο έλεγχος βασισμένος σε ρόλους είναι δύο μοτίβα χρήσιμα για την εφαρμογή ελέγχου πρόσβασης σε έξυπνα συμβόλαια:
Μοτίβο Ownable
Στο μοτίβο Ownable, μια διεύθυνση ορίζεται ως «ιδιοκτήτης» του συμβολαίου κατά τη διαδικασία δημιουργίας ενός συμβολαίου. Οι προστατευμένες συναρτήσεις ανατίθενται σε έναν τροποποιητή OnlyOwner
, ο οποίος εξασφαλίζει ότι το συμβόλαιο εξακριβώνει την ταυτότητα της διεύθυνσης κλήσης πριν εκτελέσει τη συνάρτηση. Οι κλήσεις σε προστατευμένες συναρτήσεις από άλλες διευθύνσεις, εκτός από τον ιδιοκτήτη του συμβολαίου, πάντα επιστρέφουν αποτέλεσμα, αποτρέποντας την ανεπιθύμητη πρόσβαση.
Έλεγχος πρόσβασης βάσει ρόλων
Η καταχώριση μιας μεμονωμένης διεύθυνσης ως Owner
σε ένα έξυπνο συμβόλαιο εισάγει τον κίνδυνο συγκεντρωτισμού και αντιπροσωπεύει ένα μοναδικό σημείο αποτυχίας. Εάν τα κλειδιά λογαριασμού του ιδιοκτήτη παραβιαστούν, οι επιτιθέμενοι μπορούν να επιτεθούν στην ιδιοκτησία του συμβολαίου. Γι' αυτό, η χρήση ενός μοτίβου ελέγχου πρόσβασης βασισμένου σε ρόλους με πολλούς λογαριασμούς διαχείρισης μπορεί να είναι μια καλύτερη επιλογή.
Στον έλεγχο πρόσβασης που βασίζεται σε ρόλους, η πρόσβαση σε ευαίσθητες συναρτήσεις κατανέμεται μεταξύ μιας ομάδας έμπιστων συμμετεχόντων. Για παράδειγμα, ένας λογαριασμός μπορεί να είναι υπεύθυνος για τη δημιουργία token, ενώ ένας άλλος λογαριασμός εκτελεί αναβαθμίσεις ή θέτει σε παύση το συμβόλαιο. Η αποκέντρωση του ελέγχου πρόσβασης με αυτόν τον τρόπο εξαλείφει τα μοναδικά σημεία αποτυχίας και μειώνει τις παραδοχές εμπιστοσύνης για τους χρήστες.
Χρήση πορτοφολιών πολλαπλών υπογραφών
Μια άλλη προσέγγιση για την εφαρμογή ασφαλούς ελέγχου πρόσβασης είναι η χρήση ενός λογαριασμού πολλαπλών υπογραφών για τη διαχείριση ενός συμβολαίου. Σε αντίθεση με έναν κανονικό EOA, οι λογαριασμοί πολλαπλών υπογραφών ανήκουν σε πολλές οντότητες και απαιτούν υπογραφές από έναν ελάχιστο αριθμό λογαριασμών, ας πούμε από 3 στους 5, για την εκτέλεση συναλλαγών.
Η χρήση ενός λογαριασμού πολλαπλών υπογραφών για τον έλεγχο πρόσβασης εισάγει ένα επιπλέον επίπεδο ασφάλειας, καθώς οι ενέργειες του συμβολαίου απαιτούν τη συγκατάθεση πολλών μερών. Αυτό είναι ιδιαίτερα χρήσιμο εάν είναι απαραίτητη η χρήση του μοτίβου Ownable, καθώς καθιστά πιο δύσκολο για έναν επιτιθέμενο ή απείθαρχο χρήστη να χειραγωγήσει ευαίσθητες συναρτήσεις συμβολαίων για κακόβουλους σκοπούς.
2. Χρήση εντολών require(), assert() και revert() για την προστασία των λειτουργιών του συμβολαίου
Όπως αναφέρθηκε, ο καθένας μπορεί να καλεί δημόσιες συναρτήσεις στο έξυπνο συμβόλαιό σας μόλις αυτό αναπτυχθεί στο blockchain. Δεδομένου ότι δεν μπορείτε να γνωρίζετε εκ των προτέρων πώς οι εξωτερικοί λογαριασμοί θα αλληλεπιδράσουν με ένα συμβόλαιο, θα πρέπει ιδανικά να εφαρμόσετε εσωτερικές δικλείδες ασφαλείας κατά των προβληματικών επιχειρησιακών λειτουργιών πριν από την ανάπτυξη. Μπορείτε να επιβάλετε τη σωστή συμπεριφορά στα έξυπνα συμβόλαια χρησιμοποιώντας τις εντολές require()
, assert()
και revert()
για να προκαλέσετε εξαιρέσεις και να αναστρέψετε τις αλλαγές κατάστασης εάν η εκτέλεση δεν ικανοποιεί ορισμένες απαιτήσεις.
require()
: Το require
ορίζεται στην αρχή των συναρτήσεων και εξασφαλίζει ότι πληρούνται οι προκαθορισμένες συνθήκες πριν εκτελεστεί η καλούμενη συνάρτηση. Μια εντολή require
μπορεί να χρησιμοποιηθεί για την επικύρωση στοιχείων εισόδου του χρήστη, τον έλεγχο μεταβλητών κατάστασης ή τον έλεγχο ταυτότητας της διεύθυνσης κλήσης πριν προχωρήσετε σε μια συνάρτηση.
assert()
: Το assert()
χρησιμοποιείται για τον εντοπισμό εσωτερικών σφαλμάτων και τον έλεγχο για παραβιάσεις «αμετάβλητων» του κώδικά σας. Ένα αμετάβλητο είναι μια λογική διαπίστωση σχετικά με την κατάσταση ενός συμβολαίου που θα πρέπει να ισχύει για όλες τις εκτελέσεις συναρτήσεων. Ένα παράδειγμα αμεταβλήτου είναι η μέγιστη συνολική προσφορά ή ισορροπία ενός συμβολαίου κρυπτονομίσματος. Η χρήση του assert()
διασφαλίζει ότι το συμβόλαιό σας δεν φτάνει ποτέ σε μια ευάλωτη κατάσταση και εάν το κάνει, όλες οι αλλαγές στις μεταβλητές κατάστασης αναιρούνται.
revert()
: Το revert()
μπορεί να χρησιμοποιηθεί σε μια εντολή if-else που προκαλεί μια εξαίρεση εάν η απαιτούμενη συνθήκη δεν ικανοποιείται. Το παρακάτω παράδειγμα συμβολαίου χρησιμοποιεί το revert()
για την προφύλαξη της εκτέλεσης των συναρτήσεων:
1pragma solidity ^0.8.4;23contract VendingMachine {4 address owner;5 error Unauthorized();6 function buy(uint amount) public payable {7 if (amount > msg.value / 2 ether)8 revert("Not enough Ether provided.");9 // Perform the purchase.10 }11 function withdraw() public {12 if (msg.sender != owner)13 revert Unauthorized();1415 payable(msg.sender).transfer(address(this).balance);16 }17}Εμφάνιση όλων
3. Δοκιμή έξυπνων συμβολαίων και επαλήθευση ορθότητας κώδικα
Η αμεταβλητότητα του κώδικα που εκτελείται στο Εικονικό μηχάνημα του Ethereum (Ethereum Virtual Machine) σημαίνει ότι τα έξυπνα συμβόλαια απαιτούν υψηλότερο επίπεδο αξιολόγησης ποιότητας κατά τη φάση ανάπτυξης. Η υποβολή του συμβολαίου σας σε εκτεταμένη δοκιμή και η παρακολούθησή του για τυχόν απροσδόκητα αποτελέσματα θα βελτιώσει σημαντικά την ασφάλεια και θα προστατεύσει τους χρήστες σας μακροπρόθεσμα.
Η συνήθης μέθοδος είναι να γράψετε μικρές μονάδες δοκιμής χρησιμοποιώντας δεδομένα προσομοίωσης τα οποία αναμένεται να λάβει το συμβόλαιο από τους χρήστες. Η δοκιμή μονάδας είναι καλή για τη δοκιμή της λειτουργικότητας ορισμένων συναρτήσεων και τη διασφάλιση ότι ένα έξυπνο συμβόλαιο λειτουργεί όπως αναμένεται.
Δυστυχώς, η δοκιμή μονάδας είναι ελάχιστα αποτελεσματική για τη βελτίωση της ασφάλειας των έξυπνων συμβολαίων όταν χρησιμοποιείται απομονωμένα. Μια δοκιμή μονάδας μπορεί να αποδείξει ότι μια συνάρτηση εκτελείται σωστά για προσομοιωμένα δεδομένα. Ωστόσο, οι δοκιμές μονάδας είναι τόσο αποτελεσματικές όσο οι δοκιμές που γράφονται. Αυτό καθιστά δύσκολη την ανίχνευση περιπτώσεων εξαίρεσης και ευπαθειών που διέφυγαν της προσοχής και θα μπορούσαν να παραβιάσουν την ασφάλεια του έξυπνου συμβολαίου σας.
Μια καλύτερη προσέγγιση είναι ο συνδυασμός της δοκιμής μονάδας με τη δοκιμή βασισμένη σε ιδιότητες που πραγματοποιείται χρησιμοποιώντας στατική και δυναμική ανάλυση. Η στατική ανάλυση βασίζεται σε αναπαραστάσεις χαμηλού επιπέδου, όπως τα γραφήματα ροής ελέγχου(opens in a new tab) και τα δέντρα αφηρημένης σύνταξης(opens in a new tab), για να αναλύσει τις προσβάσιμες καταστάσεις προγράμματος και διαδρομές εκτέλεσης. Εν τω μεταξύ, οι τεχνικές δυναμικής ανάλυσης, όπως το fuzzing έξυπνων συμβολαίων(opens in a new tab), εκτελούν κώδικα συμβολαίου με τυχαίες τιμές εισόδου για να εντοπίσουν λειτουργίες που παραβιάζουν τις ιδιότητες ασφαλείας.
Η τυπική επαλήθευση είναι μια άλλη τεχνική για την επαλήθευση ιδιοτήτων ασφαλείας σε έξυπνα συμβόλαια. Σε αντίθεση με τη συνήθη δοκιμή, η τυπική επαλήθευση μπορεί να αποδείξει με βεβαιότητα την απουσία σφαλμάτων σε ένα έξυπνο συμβόλαιο. Αυτό επιτυγχάνεται μέσω της δημιουργίας μιας τυπικής προδιαγραφής που καταγράφει τις επιθυμητές ιδιότητες ασφαλείας και της απόδειξης ότι ένα τυπικό μοντέλο των συμβολαίων τηρεί αυτή την προδιαγραφή.
4. Ζητήστε μια ανεξάρτητη αξιολόγηση του κώδικά σας
Μετά τη δοκιμή του συμβολαίου σας, είναι καλό να ζητήσετε από άλλους να ελέγξουν τον πηγαίο κώδικα για τυχόν ζητήματα ασφάλειας. Οι δοκιμές δεν θα αποκαλύψουν κάθε ελάττωμα σε ένα έξυπνο συμβόλαιο, αλλά η λήψη μιας ανεξάρτητης αξιολόγησης αυξάνει την πιθανότητα εντοπισμού ευπαθειών.
Έλεγχοι
Ένας τρόπος διεξαγωγής μιας ανεξάρτητης αξιολόγησης κώδικα είναι η ανάθεση της παροχής ελέγχου έξυπνου συμβολαίου. Οι ελεγκτές διαδραματίζουν σημαντικό ρόλο στη διασφάλιση ότι τα έξυπνα συμβόλαια είναι ασφαλή και απαλλαγμένα από ελαττώματα ποιότητας και σφάλματα σχεδιασμού.
Ωστόσο, θα πρέπει να αποφύγετε τη μεταχείριση των ελέγχων ως πανάκεια. Οι έλεγχοι έξυπνων συμβολαίων δεν θα εντοπίσουν κάθε σφάλμα και σχεδιάζονται κυρίως για να παρέχουν έναν επιπλέον γύρο αναθεωρήσεων, ο οποίος μπορεί να βοηθήσει στην ανίχνευση προβλημάτων που παραλείφθηκαν από τους προγραμματιστές κατά την αρχική ανάπτυξη και δοκιμή. Θα πρέπει επίσης να ακολουθείτε τις βέλτιστες πρακτικές για την εργασία με ελεγκτές, όπως η σωστή τεκμηρίωση του κώδικα και η προσθήκη ενσωματωμένων σχολίων, για να μεγιστοποιήσετε το όφελος ενός ελέγχου έξυπνου συμβολαίου.
- Συμβουλές & κόλπα ελέγχου έξυπνων συμβολαίων(opens in a new tab) — @tinchoabbate
- Αξιοποιήστε στο μέγιστο τον έλεγχό σας(opens in a new tab) — Inference
Εύρεση σφαλμάτων
Μια άλλη προσέγγιση για την εφαρμογή εξωτερικών ελέγχων κώδικα είναι η δημιουργία ενός προγράμματος ανταμοιβής για την ανεύρεση ευπαθειών. Πρόκειται για μια χρηματική ανταμοιβή που δίνεται σε άτομα (συνήθως καλόβουλους hacker) που ανακαλύπτουν ευπάθειες σε μια εφαρμογή.
Όταν χρησιμοποιούνται σωστά, οι ανταμοιβές αυτές δίνουν κίνητρα στα μέλη της κοινότητας των hacker να επιθεωρούν τον κώδικά σας προς εντοπισμό κρίσιμων τρωτοτήτων. Ένα πραγματικό παράδειγμα είναι το «σφάλμα των άπειρων χρημάτων» που θα επέτρεπε σε έναν επιτιθέμενο να δημιουργήσει απεριόριστη ποσότητα Ether στο Optimism(opens in a new tab), ένα πρωτόκολλο Επιπέδου 2 που εκτελείται στο Ethereum. Ευτυχώς, ένας καλόβουλος hacker ανακάλυψε την τρωτότητα(opens in a new tab) και ειδοποίησε την ομάδα, γεγονός που του απέφερε μια μεγάλη αμοιβή(opens in a new tab).
Μια χρήσιμη στρατηγική είναι να ορίσετε την αμοιβή στο πλαίσιο ενός προγράμματος ανταμοιβής για την ανεύρεση ευπαθειών κατ' αναλογία με το ποσό των κεφαλαίων που διακυβεύονται. Περιγράφεται ως «κλιμακούμενη ανταμοιβή για την ανεύρεση ευπαθειών(opens in a new tab)», αυτή η προσέγγιση παρέχει οικονομικά κίνητρα στα άτομα ώστε να αποκαλύπτουν υπεύθυνα τις ευπάθειες αντί να τις εκμεταλλεύονται.
5. Ακολουθήστε τις βέλτιστες πρακτικές κατά την ανάπτυξη έξυπνων συμβολαίων
Η ύπαρξη ελέγχων και προγραμμάτων ανταμοιβής για την ανεύρεση ευπαθειών δεν σας απαλλάσσει από την ευθύνη να γράφετε κώδικα υψηλής ποιότητας. Η καλή ασφάλεια των έξυπνων συμβολαίων ξεκινά με την τήρηση των κατάλληλων διαδικασιών σχεδιασμού και ανάπτυξης:
Αποθηκεύστε όλο τον κώδικα σε ένα σύστημα ελέγχου εκδόσεων, όπως το git.
Κάντε όλες τις τροποποιήσεις κώδικα μέσω αιτημάτων έλξης.
Βεβαιωθείτε ότι τα αιτήματα έλξης έχουν τουλάχιστον έναν ανεξάρτητο αξιολογητή. Εάν εργάζεστε μόνοι σας σε ένα έργο, σκεφτείτε να βρείτε άλλους προγραμματιστές και να ανταλλάξετε αξιολογήσεις κώδικα.
Χρησιμοποιήστε ένα περιβάλλον ανάπτυξης για τη δοκιμή, τη μεταγλώττιση και την ανάπτυξη έξυπνων συμβολαίων.
Εκτελέστε τον κώδικά σας μέσω βασικών εργαλείων ανάλυσης κώδικα, όπως το Cyfrin Aaderyn(opens in a new tab), το Mythril και το Slither. Ιδανικά, θα πρέπει να το κάνετε αυτό πριν συγχωνευθεί κάθε αίτημα έλξης και να συγκρίνετε τις διαφορές στην έξοδο.
Βεβαιωθείτε ότι ο κώδικάς σας μεταγλωττίζεται χωρίς σφάλματα και ότι ο μεταγλωττιστής Solidity δεν αναφέρει προειδοποιήσεις.
Τεκμηριώστε σωστά τον κώδικά σας (χρησιμοποιώντας το NatSpec(opens in a new tab)) και περιγράψτε λεπτομέρειες σχετικά με την αρχιτεκτονική του συμβολαίου σε εύκολα κατανοητή γλώσσα. Αυτό θα διευκολύνει τους άλλους στον έλεγχο και την αξιολόγηση του κώδικά σας.
6. Υλοποίηση robust Σχεδίων Ανάκτησης Καταστροφών
Ο σχεδιασμός ασφαλών ελέγχων πρόσβασης, η εφαρμογή τροποποιητών συναρτήσεων και άλλες προτάσεις μπορούν να βελτιώσουν την ασφάλεια των έξυπνων συμβολαίων, αλλά δεν μπορούν να αποκλείσουν τη δυνατότητα κακόβουλων εκμεταλλεύσεων. Η δημιουργία ασφαλών έξυπνων συμβολαίων απαιτεί «προετοιμασία για αποτυχία» και ένα εφεδρικό σχέδιο για αποτελεσματική αντίδραση σε επιθέσεις. Ένα σωστό σχέδιο ανάκτησης σε περίπτωση καταστροφής θα ενσωματώσει ορισμένα ή όλα τα ακόλουθα στοιχεία:
Αναβαθμίσεις συμβολαίων
Παρότι οι έξυπνες συμβάσεις Ethereum είναι αμετάβλητες από προεπιλογή, είναι δυνατό να επιτευχθεί κάποιος βαθμός μεταβλητότητας χρησιμοποιώντας μοτίβα αναβάθμισης. Οι αναβαθμίσεις συμβολαίων είναι απαραίτητες σε περιπτώσεις όπου μια κρίσιμη τρωτότητα καθιστά μη χρησιμοποιήσιμο το παλιό συμβόλαιο σας και η ανάπτυξη νέας λογικής είναι η πιο εφικτή επιλογή.
Οι μηχανισμοί αναβάθμισης συμβολαίων λειτουργούν διαφορετικά, αλλά το «μοτίβο proxy» είναι μία από τις πιο δημοφιλείς προσεγγίσεις για την αναβάθμιση έξυπνων συμβολαίων. Τα μοτίβα proxy(opens in a new tab) διαχωρίζουν την κατάσταση και τη λογική μιας εφαρμογής μεταξύ δύο συμβολαίων. Το πρώτο συμβόλαιο (που ονομάζεται «συμβόλαιο proxy») αποθηκεύει μεταβλητές κατάστασης (π.χ. υπόλοιπα χρηστών), ενώ το δεύτερο συμβόλαιο (που ονομάζεται «συμβόλαιο λογικής») διατηρεί τον κώδικα για την εκτέλεση συναρτήσεων συμβολαίου.
Οι λογαριασμοί αλληλεπιδρούν με το συμβόλαιο proxy, το οποίο αποστέλλει όλες τις κλήσεις συνάρτησης στο συμβόλαιο λογικής χρησιμοποιώντας τη χαμηλού επιπέδου κλήση delegatecall()
(opens in a new tab). Σε αντίθεση με μια κανονική κλήση μηνύματος, το delegatecall()
διασφαλίζει ότι ο κώδικας που εκτελείται στη διεύθυνση του συμβολαίου λογικής εκτελείται στο πλαίσιο του συμβολαίου κλήσης. Αυτό σημαίνει ότι το συμβόλαιο λογικής θα γράφει πάντα στην αποθήκευση του proxy (αντί για τη δική του αποθήκευση) και οι αρχικές τιμές του msg.sender
και msg.value
διατηρούνται.
Οι κλήσεις αντικατάστασης στο συμβόλαιο λογικής απαιτούν την αποθήκευση της διεύθυνσής του στην αποθήκευση του συμβολαίου proxy. Κατά συνέπεια, η αναβάθμιση της λογικής του συμβολαίου είναι απλώς θέμα ανάπτυξης ενός άλλου συμβολαίου λογικής και αποθήκευσης της νέας διεύθυνσης στο συμβόλαιο proxy. Καθώς οι επόμενες κλήσεις στο συμβόλαιο proxy δρομολογούνται αυτόματα στο νέο συμβόλαιο λογικής, θα έχετε «αναβαθμίσει» το συμβόλαιο χωρίς να τροποποιήσετε πραγματικά τον κώδικα.
Περισσότερα για την αναβάθμιση συμβολαίων.
Επείγοντες τερματισμοί
Όπως αναφέρθηκε, δεν είναι δυνατόν ο εκτενής έλεγχος και η εκτέλεση δοκιμών να ανακαλύψουν όλα τα σφάλματα σε ένα έξυπνο συμβόλαιο. Εάν εμφανιστεί μια ευπάθεια στον κώδικά σας μετά την ανάπτυξη, η επιδιόρθωση του είναι αδύνατη, γιατί δεν μπορείτε να αλλάξετε τον κώδικα που εκτελείται στη διεύθυνση του συμβολαίου. Επίσης, οι μηχανισμοί αναβάθμισης (π.χ. μοτίβα proxy) μπορεί να χρειαστούν χρόνο για να εφαρμοστούν (συχνά απαιτούν έγκριση από διαφορετικά μέρη), κάτι που δίνει στους επιτιθέμενους περισσότερο χρόνο για να προκαλέσουν περισσότερες ζημιές.
Η πυρηνική επιλογή είναι να εφαρμόσετε μια λειτουργία «επείγοντος τερματισμού» που αποκλείει τις κλήσεις σε ευάλωτες συναρτήσεις σε ένα συμβόλαιο. Οι επείγοντες τερματισμοί συνήθως περιλαμβάνουν τα ακόλουθα στοιχεία:
Μια καθολική μεταβλητή Boolean που υποδεικνύει εάν το έξυπνο συμβόλαιο βρίσκεται σε κατάσταση διακοπής ή όχι. Αυτή η μεταβλητή ορίζεται σε
false
κατά τη ρύθμιση της σύμβασης, αλλά θα επανέλθει σεtrue
μόλις σταματήσει το συμβόλαιο.Συναρτήσεις που αναφέρονται στη μεταβλητή Boolean κατά την εκτέλεσή τους. Τέτοιες συναρτήσεις είναι προσβάσιμες όταν το έξυπνο συμβόλαιο δεν έχει σταματήσει και γίνεται απροσπέλαστο όταν ενεργοποιηθεί η λειτουργία επείγοντος τερματισμού.
Μια οντότητα που έχει πρόσβαση στη λειτουργία επείγοντος τερματισμού, η οποία ορίζει τη μεταβλητή Boolean σε
true
. Για να αποτρέπονται κακόβουλες ενέργειες, οι κλήσεις σε αυτή τη συνάρτηση μπορούν να περιοριστούν σε μια αξιόπιστη διεύθυνση (π.χ. του κατόχου της σύμβασης).
Μόλις το συμβόλαιο ενεργοποιήσει τον επείγοντα τερματισμό, δεν θα είναι δυνατή η κλήση ορισμένων συναρτήσεων. Αυτό επιτυγχάνεται με τη χρήση επιλεγμένων συναρτήσεων σε έναν τροποποιητή που αναφέρεται στην καθολική μεταβλητή. Παρακάτω παρατίθεται ένα παράδειγμα(opens in a new tab) που περιγράφει μια εφαρμογή αυτού του προτύπου σε συμβόλαια:
1// This code has not been professionally audited and makes no promises about safety or correctness. Χρησιμοποιήστε με δική σας ευθύνη.23contract EmergencyStop {45 bool isStopped = false;67 modifier stoppedInEmergency {8 require(!isStopped);9 _;10 }1112 modifier onlyWhenStopped {13 require(isStopped);14 _;15 }1617 modifier onlyAuthorized {18 // Check for authorization of msg.sender here19 _;20 }2122 function stopContract() public onlyAuthorized {23 isStopped = true;24 }2526 function resumeContract() public onlyAuthorized {27 isStopped = false;28 }2930 function deposit() public payable stoppedInEmergency {31 // Deposit logic happening here32 }3334 function emergencyWithdraw() public onlyWhenStopped {35 // Emergency withdraw happening here36 }37}Εμφάνιση όλωνΑντιγραφή
Αυτό το παράδειγμα δείχνει τα βασικά χαρακτηριστικά των έκτακτων διακοπών:
Το
isStopped
είναι ένα Boolean που αξιολογείται σεfalse
στην αρχή καιtrue
όταν το συμβόλαιο εισέρχεται σε κατάσταση έκτακτης ανάγκης.Οι τροποποιητές συνάρτησης
onlyWhenStopped
καιstoppedInEmergency
ελέγχουν τη μεταβλητήisStopped
. ΤοstoppedInEmergency
χρησιμοποιείται για τον έλεγχο συναρτήσεων που θα πρέπει να είναι απροσπέλαστες όταν το συμβόλαιο είναι ευάλωτο (π.χ.deposit()
). Οι κλήσεις σε αυτές τις συναρτήσεις απλά θα επιστρέφουν.
Το onlyWhenStopped
χρησιμοποιείται για συναρτήσεις που θα μπορούν να καλούνται κατά τη διάρκεια μιας έκτακτης ανάγκης (π.χ. emergencyWithdraw()
). Τέτοιες συναρτήσεις μπορούν να βοηθήσουν στην επίλυση της κατάστασης, εξ ου και η εξαίρεση τους από τη λίστα «περιορισμένες συναρτήσεις».
Η χρήση μιας λειτουργίας επείγοντος τερματισμού παρέχει ένα αποτελεσματικό προσωρινό μέτρο για την αντιμετώπιση σοβαρών ευπαθειών στο έξυπνο συμβόλαιό σας. Ωστόσο, αυξάνει την ανάγκη οι χρήστες να εμπιστεύονται τους προγραμματιστές ότι δεν θα το ενεργοποιούν για ιδιοτελείς λόγους. Για τον σκοπό αυτό, πιθανές λύσεις είναι η αποκέντρωση του ελέγχου του επείγοντα τερματισμού μέσω της υποβολής του σε έναν μηχανισμό ψηφοφορίας εντός αλυσίδας, χρονικού κλειδώματος ή έγκρισης από ένα πορτοφόλι πολλαπλών υπογραφών.
Παρακολούθηση γεγονότος
Τα συμβάντα(opens in a new tab) σάς επιτρέπουν να παρακολουθείτε κλήσεις σε συναρτήσεις έξυπνου συμβολαίου και να παρακολουθείτε αλλαγές στις μεταβλητές κατάστασης. Καλό θα είναι να προγραμματίσετε το έξυπνο συμβόλαιό σας να εκπέμπει ένα συμβάν κάθε φορά που κάποιο μέρος κάνει μια κρίσιμη για την ασφάλεια ενέργεια (π.χ. ανάληψη κεφαλαίων).
Η καταγραφή συμβάντων και η παρακολούθησή τους εκτός αλυσίδας παρέχει πληροφορίες σχετικά με τις λειτουργίες του συμβολαίου και συμβάλλει στην ταχύτερη ανακάλυψη κακόβουλων ενεργειών. Αυτό σημαίνει ότι η ομάδα σας μπορεί να ανταποκριθεί πιο γρήγορα στις παραβιάσεις και να λάβει μέτρα για να μετριάσει τον αντίκτυπο στους χρήστες, όπως την παύση των συναρτήσεων ή την εκτέλεση μιας αναβάθμισης.
Μπορείτε επίσης να επιλέξετε ένα έτοιμο εργαλείο παρακολούθησης που προωθεί αυτόματα ειδοποιήσεις κάθε φορά που κάποιος αλληλεπιδρά με τα συμβόλαιά σας. Αυτά τα εργαλεία θα σας επιτρέψουν να δημιουργήσετε προσαρμοσμένες ειδοποιήσεις με βάση διαφορετικούς παράγοντες σκανδάλισης, όπως ο όγκος συναλλαγών, η συχνότητα κλήσεων συναρτήσεων ή οι συγκεκριμένες συναρτήσεις που εμπλέκονται. Για παράδειγμα, θα μπορούσατε να προγραμματίσετε μια ειδοποίηση που εμφανίζεται όταν το ποσό που αναλήφθηκε σε μία μόνο συναλλαγή υπερβαίνει ένα συγκεκριμένο όριο.
7. Σχεδίαση ασφαλών συστημάτων διακυβέρνησης
Μπορεί να θέλετε να αποκεντρώσετε την εφαρμογή σας μεταβιβάζοντας τον έλεγχο των βασικών έξυπνων συμβολαίων στα μέλη της κοινότητας. Σε αυτή την περίπτωση, το σύστημα έξυπνων συμβολαίων θα περιλαμβάνει μια ενότητα διακυβέρνησης, δηλαδή έναν μηχανισμό που επιτρέπει στα μέλη της κοινότητας να εγκρίνουν διοικητικές ενέργειες μέσω ενός συστήματος διακυβέρνησης εντός αλυσίδας. Για παράδειγμα, μια πρόταση αναβάθμισης ενός συμβολαίου proxy σε μια νέα υλοποίηση μπορεί να τεθεί σε ψηφοφορία από τους κατόχους token.
Η αποκεντρωμένη διακυβέρνηση μπορεί να είναι ευεργετική, ειδικά επειδή ευθυγραμμίζει τα συμφέροντα των προγραμματιστών και των τελικών χρηστών. Ωστόσο, οι μηχανισμοί διακυβέρνησης των έξυπνων συμβολαίων ενδέχεται να εισαγάγουν νέους κινδύνους εάν εφαρμοστούν εσφαλμένα. Ένα πιθανό σενάριο είναι η περίπτωση κατά την οποία ένας επιτιθέμενος αποκτά τεράστιο δικαίωμα ψήφου (μετρούμενο σε αριθμό token που κατέχει) λαμβάνοντας ένα flash loan (στιγμιαίο δάνειο) και προωθεί μια κακόβουλη πρόταση.
Ένας τρόπος αποτροπής προβλημάτων που σχετίζονται με τη διακυβέρνηση εντός της αλυσίδας είναι η χρήση ενός timelock(opens in a new tab). Ένα timelock (χρονοδιακόπτης) εμποδίζει ένα έξυπνο συμβόλαιο να εκτελέσει ορισμένες ενέργειες έως ότου παρέλθει ένα συγκεκριμένο χρονικό διάστημα. Άλλες στρατηγικές περιλαμβάνουν την εκχώρηση ενός «βάρους ψήφου» σε κάθε token με βάση το πόσο καιρό έχει κλειδωθεί ή τη μέτρηση της δύναμης του δικαιώματος ψήφου μιας διεύθυνσης σε μια ιστορική περίοδο (για παράδειγμα, 2-3 μπλοκ στο παρελθόν) αντί για το τρέχον μπλοκ. Και οι δύο μέθοδοι μειώνουν τη δυνατότητα γρήγορης συγκέντρωσης δύναμης ψήφου για να αλλάξουν οι ψήφοι εντός αλυσίδας.
Περισσότερα σχετικά με τον σχεδιασμό ασφαλών συστημάτων διακυβέρνησης(opens in a new tab), διαφορετικούς μηχανισμούς ψηφοφορίας σε DAO(opens in a new tab) και τους κοινούς φορείς επίθεσης DAO που αξιοποιούν το DeFi(opens in a new tab) στους κοινόχρηστους συνδέσμους.
8. Μείωση της πολυπλοκότητας του κώδικα στο ελάχιστο
Οι παραδοσιακοί προγραμματιστές λογισμικού είναι εξοικειωμένοι με την αρχή της απλότητας στον σχεδιασμό KISS («keep it simple, stupid»), η οποία συνιστά να αποφεύγεται η εισαγωγή περιττής πολυπλοκότητας στον σχεδιασμό λογισμικού. Αυτό ακολουθεί την παγιωμένη σκέψη ότι «τα πολύπλοκα συστήματα αποτυγχάνουν με πολύπλοκους τρόπους» και είναι πιο επιρρεπή σε δαπανηρά σφάλματα.
Το να διατηρείς τα πράγματα απλά είναι ιδιαίτερα σημαντικό κατά τη συγγραφή έξυπνων συμβολαίων, δεδομένου ότι τα έξυπνα συμβόλαια ελέγχουν ενδεχομένως μεγάλες ποσότητες αξίας. Μια συμβουλή για την επίτευξη απλότητας κατά τη συγγραφή έξυπνων συμβολαίων είναι η επαναχρησιμοποίηση υπαρχουσών βιβλιοθηκών, όπως οι OpenZeppelin Contracts(opens in a new tab), όπου είναι δυνατόν. Επειδή αυτές οι βιβλιοθήκες έχουν ελεγχθεί και δοκιμαστεί εκτενώς από προγραμματιστές, η χρήση τους μειώνει τις πιθανότητες εισαγωγής σφαλμάτων γράφοντας νέες λειτουργίες από την αρχή.
Μια άλλη συνηθισμένη συμβουλή είναι να γράφετε μικρές συναρτήσεις και να διατηρείτε τα συμβόλαια διαχωρισμένα σε επιμέρους μονάδες, χωρίζοντας τη λογική επιχείρησης σε πολλά συμβόλαια. Η συγγραφή ενός απλούστερου κώδικα όχι μόνο μειώνει την επιφάνεια επίθεσης σε ένα έξυπνο συμβόλαιο, αλλά καθιστά επίσης ευκολότερο τον συλλογισμό σχετικά με τη σωστή λειτουργία του συνολικού συστήματος και την έγκαιρη ανίχνευση πιθανών σφαλμάτων σχεδιασμού.
9. Προστασία από κοινές ευπάθειες έξυπνων συμβολαίων
Επανεισαγωγή
Η EVM δεν επιτρέπει τη σύνδρομη εκτέλεση, που σημαίνει ότι δύο συμβόλαια που εμπλέκονται σε μια κλήση μηνύματος δεν μπορούν να εκτελεστούν ταυτόχρονα. Μια εξωτερική κλήση παύει την εκτέλεση και τη μνήμη του συμβολαίου κλήσης μέχρι να επιστρέψει η κλήση, οπότε η εκτέλεση συνεχίζεται κανονικά. Αυτή η διαδικασία μπορεί να περιγραφεί επίσημα ως μεταφορά της ροής ελέγχου(opens in a new tab) σε άλλο συμβόλαιο.
Αν και κατά κύριο λόγο είναι ακίνδυνη, η μεταφορά ροής ελέγχου σε μη αξιόπιστα συμβόλαια μπορεί να προκαλέσει προβλήματα, όπως η επανεισαγωγή. Μια επίθεση επανεισαγωγής συμβαίνει όταν ένα κακόβουλο συμβόλαιο καλεί ξανά ένα ευάλωτο συμβόλαιο πριν ολοκληρωθεί η αρχική κλήση της συνάρτησης. Αυτός ο τύπος επίθεσης εξηγείται καλύτερα με ένα παράδειγμα.
Εξετάστε ένα απλό έξυπνο συμβόλαιο («Θύμα») που επιτρέπει σε οποιονδήποτε να καταθέτει και να κάνει ανάληψη Ether:
1// This contract is vulnerable. Do not use in production23contract Victim {4 mapping (address => uint256) public balances;56 function deposit() external payable {7 balances[msg.sender] += msg.value;8 }910 function withdraw() external {11 uint256 amount = balances[msg.sender];12 (bool success, ) = msg.sender.call.value(amount)("");13 require(success);14 balances[msg.sender] = 0;15 }16}Εμφάνιση όλωνΑντιγραφή
Αυτό το συμβόλαιο εκθέτει μια συνάρτηση withdraw()
για να επιτρέψει στους χρήστες να αποσύρουν ETH που έχουν καταθέσει προηγουμένως στο συμβόλαιο. Όταν επεξεργάζεται μια ανάληψη, το συμβόλαιο εκτελεί τις ακόλουθες λειτουργίες:
- Ελέγχει το υπόλοιπο ETH του χρήστη
- Στέλνει χρήματα στη διεύθυνση κλήσης
- Επαναφέρει το υπόλοιπό του στο 0, αποτρέποντας περαιτέρω αναλήψεις από τον χρήστη
Η συνάρτηση withdraw()
στο συμβόλαιο Victim
ακολουθεί ένα πρότυπο «ελέγχων-αλληλεπιδράσεων-επιδράσεων». Ελέγχει εάν οι συνθήκες που είναι απαραίτητες για την εκτέλεση πληρούνται (δηλαδή, ο χρήστης έχει θετικό υπόλοιπο ETH) και εκτελεί την αλληλεπίδραση στέλνοντας ETH στη διεύθυνση του καλούντος, πριν εφαρμόσει τις επιδράσεις της συναλλαγής (δηλαδή, μείωση του υπολοίπου του χρήστη).
Εάν η withdraw()
κληθεί από έναν λογαριασμό εξωτερικού κατόχου (EOA), η συνάρτηση εκτελείται όπως αναμένεται: η msg.sender.call.value()
στέλνει ETH στον καλούντα. Ωστόσο, εάν το msg.sender
είναι μια διεύθυνση έξυπνου συμβολαίου που καλεί την withdraw()
, η αποστολή χρημάτων χρησιμοποιώντας msg.sender.call.value()
θα ενεργοποιήσει επίσης την εκτέλεση του κώδικα που είναι αποθηκευμένος σε αυτή τη διεύθυνση.
Φανταστείτε ότι αυτός είναι ο κώδικας που αναπτύχθηκε στη διεύθυνση του συμβολαίου:
1 contract Attacker {2 function beginAttack() external payable {3 Victim(victim_address).deposit.value(1 ether)();4 Victim(victim_address).withdraw();5 }67 function() external payable {8 if (gasleft() > 40000) {9 Victim(victim_address).withdraw();10 }11 }12}Εμφάνιση όλωνΑντιγραφή
Αυτό το συμβόλαιο έχει σχεδιαστεί να κάνει τρία πράγματα:
- Να αποδεχτεί κατάθεση από έναν άλλο λογαριασμό (πιθανότατα τον EOA του επιτιθέμενου)
- Να καταθέσει 1 ETH στο συμβόλαιο του Θύματος
- Να κάνει ανάληψη του 1 ETH που είναι αποθηκευμένο στο έξυπνο συμβόλαιο
Δεν υπάρχει τίποτα κακό εδώ, εκτός από το ότι ο επιτιθέμενος Attacker
έχει μια άλλη συνάρτηση που καλεί ξανά τη withdraw()
στο Victim
εάν το καύσιμο που απομένει από το εισερχόμενο msg.sender.call.value
είναι μεγαλύτερο από 40.000. Αυτό δίνει στον Attacker
τη δυνατότητα να εισέλθει ξανά στο Victim
και να αποσύρει περισσότερα χρήματα πριν ολοκληρωθεί η πρώτη κλήση της withdraw
. Ο κύκλος μοιάζει με αυτό:
1- Attacker's EOA calls `Attacker.beginAttack()` with 1 ETH2- `Attacker.beginAttack()` deposits 1 ETH into `Victim`3- `Attacker` calls `withdraw() in `Victim`4- `Victim` checks `Attacker`’s balance (1 ETH)5- `Victim` sends 1 ETH to `Attacker` (which triggers the default function)6- `Attacker` calls `Victim.withdraw()` again (note that `Victim` hasn’t reduced `Attacker`’s balance from the first withdrawal)7- `Victim` checks `Attacker`’s balance (which is still 1 ETH because it hasn’t applied the effects of the first call)8- `Victim` sends 1 ETH to `Attacker` (which triggers the default function and allows `Attacker` to reenter the `withdraw` function)9- The process repeats until `Attacker` runs out of gas, at which point `msg.sender.call.value` returns without triggering additional withdrawals10- `Victim` finally applies the results of the first transaction (and subsequent ones) to its state, so `Attacker`’s balance is set to 0Εμφάνιση όλωνΑντιγραφή
Εν συντομία: επειδή το υπόλοιπο του καλούντος δεν ορίζεται σε 0 μέχρι την ολοκλήρωση της εκτέλεσης της συνάρτησης, οι επόμενες κλήσεις θα είναι επιτυχημένες και θα επιτρέψουν στον καλούντα να κάνει ανάληψη του υπολοίπου του πολλές φορές. Αυτός ο τύπος επίθεσης μπορεί να χρησιμοποιηθεί για να απομυζήσει τα κεφάλαια ένος έξυπνου συμβολαίου, όπως συνέβη στο hack του DAO το 2016(opens in a new tab). Οι επιθέσεις επανεισόδου εξακολουθούν να αποτελούν ένα κρίσιμο ζήτημα για τα έξυπνα συμβόλαια σήμερα, όπως δείχνουν οι δημόσιες καταχωρίσεις προγραμμάτων εκμετάλλευσης τρωτότητας (exploit) επανεισόδου(opens in a new tab).
Πώς να αποτρέψετε επιθέσεις επανεισόδου
Μια προσέγγιση για την αντιμετώπιση της επανεισόδου είναι η παρακολούθηση του μοτίβου ελέγχου-επιδράσεων-αλληλεπιδράσεων(opens in a new tab). Αυτό το μοτίβο ταξινομεί την εκτέλεση των συναρτήσεων με τρόπο ώστε να έρχεται πρώτος ο κώδικας που εκτελεί τους απαραίτητους ελέγχους πριν προχωρήσει στην εκτέλεση, ακολουθούμενος από κώδικα που χειρίζεται την κατάσταση της σύμβασης, και να φτάνει τελευταίος ο κώδικα που αλληλεπιδρά με άλλες συμβάσεις ή EOA.
Το μοτίβο ελέγχου-επίδρασης-αλληλεπίδρασης χρησιμοποιείται σε μια αναθεωρημένη έκδοση του συμβολαίου Victim
που φαίνεται παρακάτω:
1contract NoLongerAVictim {2 function withdraw() external {3 uint256 amount = balances[msg.sender];4 balances[msg.sender] = 0;5 (bool success, ) = msg.sender.call.value(amount)("");6 require(success);7 }8}Αντιγραφή
Αυτό το συμβόλαιο εκτελεί έναν έλεγχο στο υπόλοιπο του χρήστη, εφαρμόζει τις επιδράσεις της συνάρτησης withdraw()
(επαναφέροντας το υπόλοιπο του χρήστη στο 0) και προχωρά στην εκτέλεση της αλληλεπίδρασης (αποστολή ETH στη διεύθυνση του χρήστη). Αυτό διασφαλίζει ότι το συμβόλαιο ενημερώνει την αποθήκευσή του πριν από την εξωτερική κλήση, εξαλείφοντας την κατάσταση επανεισόδου που επέτρεψε την πρώτη επίθεση. Το συμβόλαιο Attacker
θα μπορούσε ακόμα να καλέσει ξανά το NoLongerAVictim
, αλλά επειδή το balances[msg.sender]
έχει οριστεί σε 0, οι πρόσθετες αναλήψεις θα προκαλέσουν σφάλμα.
Μια άλλη επιλογή είναι να χρησιμοποιήσετε ένα κλείδωμα αμοιβαίου αποκλεισμού (συνήθως περιγράφεται ως «mutex») που κλειδώνει ένα μέρος της κατάστασης ενός συμβολαίου μέχρι να ολοκληρωθεί μια κλήση συνάρτησης. Αυτό υλοποιείται χρησιμοποιώντας μια μεταβλητή Boolean που ορίζεται σε true
πριν εκτελεστεί η συνάρτηση και επιστρέφει σε false
μετά την ολοκλήρωση της κλήσης. Όπως φαίνεται στο παρακάτω παράδειγμα, η χρήση ενός mutex προστατεύει μια συνάρτηση από αναδρομικές κλήσεις ενώ η αρχική κλήση εξακολουθεί να βρίσκεται υπό επεξεργασία, σταματώντας αποτελεσματικά την επανείσοδο.
1pragma solidity ^0.7.0;23contract MutexPattern {4 bool locked = false;5 mapping(address => uint256) public balances;67 modifier noReentrancy() {8 require(!locked, "Blocked from reentrancy.");9 locked = true;10 _;11 locked = false;12 }13 // This function is protected by a mutex, so reentrant calls from within `msg.sender.call` cannot call `withdraw` again.14 // The `return` statement evaluates to `true` but still evaluates the `locked = false` statement in the modifier15 function withdraw(uint _amount) public payable noReentrancy returns(bool) {16 require(balances[msg.sender] >= _amount, "No balance to withdraw.");1718 balances[msg.sender] -= _amount;19 bool (success, ) = msg.sender.call{value: _amount}("");20 require(success);2122 return true;23 }24}Εμφάνιση όλωνΑντιγραφή
Μπορείτε επίσης να χρησιμοποιήσετε ένα σύστημα πληρωμών με αίτημα(opens in a new tab) που απαιτεί από τους χρήστες να κάνουν ανάληψη κεφαλαίων από τα έξυπνα συμβόλαια, αντί για ένα σύστημα πληρωμών «ώθησης» που στέλνει κεφάλαια σε λογαριασμούς. Αυτό εξαλείφει τη δυνατότητα τυχαίας ενεργοποίησης κώδικα σε άγνωστες διευθύνσεις (και μπορεί επίσης να αποτρέψει ορισμένες επιθέσεις άρνησης υπηρεσίας).
Υποεκφορτώσεις και υπερβάσεις ακεραίων
Υπέρβαση ακεραίου συμβαίνει όταν τα αποτελέσματα μιας αριθμητικής πράξης πέφτουν εκτός του αποδεκτού εύρους τιμών, προκαλώντας την «επαναφορά» του στη χαμηλότερη προβαλλόμενη τιμή. Για παράδειγμα, ένα uint8
μπορεί να αποθηκεύσει μόνο τιμές έως 2^8-1=255. Οι αριθμητικές πράξεις που έχουν ως αποτέλεσμα τιμές μεγαλύτερες από 255
θα υπερχειλιστούν και θα επαναφέρουν το uint
στο 0
, με τρόπο παρόμοιο όπως ο χιλιομετρητής ενός αυτοκινήτου επαναφέρεται στο 0 όταν φτάσει στη μέγιστη απόσταση (999999).
Οι υποεκφορτώσεις ακεραίων συμβαίνουν για παρόμοιους λόγους: τα αποτελέσματα μιας αριθμητικής πράξης πέφτουν κάτω από το αποδεκτό εύρος. Αν υποθέσουμε ότι προσπαθήσατε να μειώσετε το 0
σε ένα uint8
, το αποτέλεσμα θα επανερχόταν απλώς στη μέγιστη προβαλλόμενη τιμή (255
).
Τόσο οι υπερβάσεις όσο και οι υποεκφορτώσεις ακεραίων μπορούν να οδηγήσουν σε απροσδόκητες αλλαγές στις μεταβλητές κατάστασης ενός συμβολαίου και να οδηγήσουν σε μη προγραμματισμένη εκτέλεση. Παρακάτω παρατίθεται ένα παράδειγμα που δείχνει πώς ένας επιτιθέμενος μπορεί να εκμεταλλευτεί την υπέρβαση ακεραίου σε ένα έξυπνο συμβόλαιο για να εκτελέσει μια μη έγκυρη λειτουργία:
1pragma solidity ^0.7.6;23// This contract is designed to act as a time vault.4// User can deposit into this contract but cannot withdraw for at least a week.5// User can also extend the wait time beyond the 1 week waiting period.67/*81. Deploy TimeLock92. Deploy Attack with address of TimeLock103. Call Attack.attack sending 1 ether. You will immediately be able to11 withdraw your ether.1213Τι συνέβη;14Attack caused the TimeLock.lockTime to overflow and was able to withdraw15before the 1 week waiting period.16*/1718contract TimeLock {19 mapping(address => uint) public balances;20 mapping(address => uint) public lockTime;2122 function deposit() external payable {23 balances[msg.sender] += msg.value;24 lockTime[msg.sender] = block.timestamp + 1 weeks;25 }2627 function increaseLockTime(uint _secondsToIncrease) public {28 lockTime[msg.sender] += _secondsToIncrease;29 }3031 function withdraw() public {32 require(balances[msg.sender] > 0, "Insufficient funds");33 require(block.timestamp > lockTime[msg.sender], "Lock time not expired");3435 uint amount = balances[msg.sender];36 balances[msg.sender] = 0;3738 (bool sent, ) = msg.sender.call{value: amount}("");39 require(sent, "Failed to send Ether");40 }41}4243contract Attack {44 TimeLock timeLock;4546 constructor(TimeLock _timeLock) {47 timeLock = TimeLock(_timeLock);48 }4950 fallback() external payable {}5152 function attack() public payable {53 timeLock.deposit{value: msg.value}();54 /*55 if t = current lock time then we need to find x such that56 x + t = 2**256 = 057 so x = -t58 2**256 = type(uint).max + 159 so x = type(uint).max + 1 - t60 */61 timeLock.increaseLockTime(62 type(uint).max + 1 - timeLock.lockTime(address(this))63 );64 timeLock.withdraw();65 }66}Εμφάνιση όλων
Πώς να αποτρέψετε τις υποεκφορτώσεις και τις υπερβάσεις ακεραίων
Από την έκδοση 0.8.0 και μετά, ο μεταγλωττιστής Solidity απορρίπτει τον κώδικα που οδηγεί σε υποεκφορτώσεις και υπερβάσεις ακεραίων. Ωστόσο, τα συμβόλαια που μεταγλωττίζονται με χαμηλότερη έκδοση μεταγλωττιστή θα πρέπει είτε να εκτελούν ελέγχους σε συναρτήσεις που περιλαμβάνουν αριθμητικές πράξεις είτε να χρησιμοποιούν μια βιβλιοθήκη (π.χ. SafeMath(opens in a new tab)) που ελέγχει για υποεκφόρτωση/υπέρβαση.
Διαχείριση Oracle
Οι Oracle προμηθεύουν πληροφορίες εκτός αλυσίδας και τις στέλνουν εντός αλυσίδας για χρήση από έξυπνα συμβόλαια. Με τις Oracle, μπορείτε να σχεδιάσετε έξυπνα συμβόλαια που διαλειτουργούν με συστήματα εκτός αλυσίδας, όπως οι κεφαλαιαγορές, επεκτείνοντας σημαντικά την εφαρμογή τους.
Αλλά αν η Oracle είναι κατεστραμμένη και στέλνει εσφαλμένες πληροφορίες εντός αλυσίδας, τα έξυπνα συμβόλαια θα εκτελεστούν με βάση εσφαλμένα δεδομένα εισόδου, κάτι που μπορεί να προκαλέσει προβλήματα. Αυτό βρίσκεται στη βάση του «προβλήματος της oracle», το οποίο αφορά το έργο της διασφάλισης ότι οι πληροφορίες από μια oracle του blockchain είναι ακριβείς, ενημερωμένες και έγκαιρες.
Μια σχετική ανησυχία ως προς την ασφάλεια είναι η χρήση μιας oracle εντός αλυσίδας, όπως ένα αποκεντρωμένο ανταλλακτήριο, για να λάβετε την τρέχουσα τιμή για ένα περιουσιακό στοιχείο. Οι πλατφόρμες δανεισμού στον κλάδο της αποκεντρωμένης οικονομίας (DeFi) το κάνουν συχνά αυτό για να προσδιορίσουν την αξία των εξασφαλίσεων ενός χρήστη για να καθορίσουν πόσα μπορούν να δανειστούν.
Οι τιμές DEX είναι συχνά ακριβείς, κυρίως λόγω των αρμπιτράζ που αποκαθιστούν την ισοτιμία στις αγορές. Ωστόσο, είναι εκτεθειμένες σε χειραγώγηση, ειδικά εάν η ενδοαλυσίδα oracle υπολογίζει τις τιμές των περιουσιακών στοιχείων με βάση τα ιστορικά πρότυπα συναλλαγών (όπως συμβαίνει συνήθως).
Για παράδειγμα, ένας επιτιθέμενος θα μπορούσε να ασκήσει επίμονα τεχνητή πίεση στην τρέχουσα τιμή ενός περιουσιακού στοιχείου λαμβάνοντας ένα flash loan ακριβώς πριν αλληλεπιδράσει με τη σύμβασή σας δανεισμού. Η ερώτηση στο DEX για την τιμή του περιουσιακού στοιχείου θα επιστρέψει μια υψηλότερη από το κανονικό τιμή (λόγω της μεγάλης «εντολής αγοράς» του επιτιθέμενου που παραμορφώνει τη ζήτηση για το περιουσιακό στοιχείο), επιτρέποντάς του να δανειστεί περισσότερα από όσο θα έπρεπε. Τέτοιες «επιθέσεις flash loan» έχουν αξιοποιηθεί για να εκμεταλλευτούν την εξάρτηση από τα oracle τιμών μεταξύ των εφαρμογών DeFi, με κόστος εκατομμυρίων σε χαμένα κεφάλαια για τα πρωτόκολλα.
Πώς να αποτρέψετε τη χειραγώγηση εκτιμήσεων
Η ελάχιστη απαίτηση για την αποφυγή χειραγώγησης εκτιμήσεων(opens in a new tab) είναι η χρήση ενός αποκεντρωμένου δικτύου εκτιμήσεων που ανακτά πληροφορίες από πολλές πηγές για να αποφύγει τα ενιαία σημεία αποτυχίας. Στις περισσότερες περιπτώσεις, οι αποκεντρωμένες εκτιμήσεις έχουν ενσωματωμένα κρυπτοοικονομικά κίνητρα για να ενθαρρύνουν τους κόμβους oracle να αναφέρουν σωστές πληροφορίες, καθιστώντας τις πιο ασφαλείς από τις κεντρικές εκτιμήσεις.
Εάν σχεδιάζετε να ανακτήσετε τις τιμές των περιουσιακών στοιχείων από μια εκτίμηση εντός αλυσίδας, σκεφτείτε να χρησιμοποιήσετε μία που να εφαρμόζει έναν μηχανισμό χρονικά σταθμισμένης μέσης τιμής (TWAP). Μια εκτίμηση TWAP(opens in a new tab) ανακτά την τιμή ενός περιουσιακού στοιχείου σε δύο διαφορετικά χρονικά σημεία (τα οποία μπορείτε να τροποποιήσετε) και υπολογίζει την τρέχουσα τιμή με βάση τη μέση τιμή που προκύπτει. Η επιλογή μεγαλύτερων χρονικών περιόδων προστατεύει το πρωτόκολλό σας από τη χειραγώγηση τιμών, καθώς οι μεγάλες εντολές που εκτελέστηκαν πρόσφατα δεν μπορούν να επηρεάσουν τις τιμές των περιουσιακών στοιχείων.
Πόροι ασφαλείας έξυπνων συμβολαίων για προγραμματιστές
Εργαλεία για ανάλυση έξυπνων συμβολαίων και επαλήθευση της ορθότητας του κώδικα
Συλλογή εργαλείων και βιβλιοθηκών - Συλλογή εργαλείων και βιβλιοθηκών αναπτυγμένων βάσει προτύπων του κλάδου για την εκτέλεση δοκιμών μονάδας, στατικής ανάλυσης και δυναμικής ανάλυσης σε έξυπνα συμβόλαια.
Εργαλεία Τυπικής Επαλήθευσης - Εργαλεία για την επαλήθευση της λειτουργικής ορθότητας σε έξυπνα συμβόλαια και τον έλεγχο αναλλοίωτων.
Υπηρεσίες Ελέγχου Έξυπνων Συμβολαίων - Κατάλογος οργανισμών που παρέχουν υπηρεσίες ελέγχου έξυπνων συμβολαίων για έργα ανάπτυξης Ethereum.
Πλατφόρμες εύρεσης σφαλμάτων - Πλατφόρμες για τον συντονισμό εύρεσης σφαλμάτων και την ανταμοιβή της υπεύθυνης αποκάλυψης κρίσιμων ευπαθειών σε έξυπνα συμβόλαια.
Fork Checker(opens in a new tab) - Ένα δωρεάν online εργαλείο για τον έλεγχο όλων των διαθέσιμων πληροφοριών σχετικά με μια διακλαδισμένη σύμβαση.
ABI Encoder(opens in a new tab) - Μια δωρεάν online υπηρεσία για την κωδικοποίηση των συναρτήσεων και των ορισμάτων κατασκευαστή του Solidity συμβολαίου σας.
Aderyn(opens in a new tab) - Στατικός αναλυτής Solidity, ο οποίος διασχίζει τα Δέντρα Αφηρημένης Σύνταξης (AST) για τον εντοπισμό ύποπτων ευπαθειών και την εκτύπωση προβλημάτων σε εύπεπτη μορφή markdown.
Εργαλεία ελέγχου έξυπνων συμβολαίων
OpenZeppelin Defender Sentinels(opens in a new tab) - Ένα εργαλείο για την αυτόματη παρακολούθηση και ανταπόκριση σε γεγονότα, συναρτήσεις και παραμέτρους συναλλαγών στα έξυπνα συμβόλαιά σας.
Tenderly Real-Time Alerting(opens in a new tab) - Ένα εργαλείο για τη λήψη ειδοποιήσεων σε πραγματικό χρόνο όταν συμβαίνουν ασυνήθιστα ή απροσδόκητα συμβάντα στα έξυπνα συμβόλαια ή τα πορτοφόλια σας.
Εργαλεία Ασφαλούς Διαχείρισης Έξυπνων Συμβολαίων
OpenZeppelin Defender Admin(opens in a new tab) - Διεπαφή για τη διαχείριση της διαχείρισης έξυπνων συμβολαίων, συμπεριλαμβανομένων των ελέγχων πρόσβασης, των αναβαθμίσεων και της παύσης.
Safe(opens in a new tab) - Πορτοφόλι έξυπνων συμβολαίων που εκτελείται στο Ethereum και απαιτεί έναν ελάχιστο αριθμό ατόμων για να εγκρίνουν μια συναλλαγή πριν αυτή μπορέσει να συμβεί (M-of-N).
OpenZeppelin Contracts(opens in a new tab) - Βιβλιοθήκες συμβολαίων για την υλοποίηση διοικητικών λειτουργιών, συμπεριλαμβανομένης της κυριότητας συμβολαίων, των αναβαθμίσεων, των ελέγχων πρόσβασης, της διακυβέρνησης, της δυνατότητας παύσης και άλλων.
Υπηρεσίες ελέγχου έξυπνων συμβολαίων
ConsenSys Diligence(opens in a new tab) - Υπηρεσία ελέγχου έξυπνων συμβολαίων που βοηθά τα έργα σε όλο το οικοσύστημα blockchain να διασφαλίσουν ότι τα πρωτόκολλά τους είναι έτοιμα προς κυκλοφορία και κατασκευασμένα για την προστασία των χρηστών.
CertiK(opens in a new tab) - Εταιρεία ασφάλειας blockchain που πρωτοπορεί στη χρήση της τεχνολογίας τυπικής επαλήθευσης αιχμής σε έξυπνα συμβόλαια και δίκτυα blockchain.
Trail of Bits(opens in a new tab) - Εταιρεία κυβερνοασφάλειας που συνδυάζει την έρευνα ασφάλειας με τη νοοτροπία του επιτιθέμενου για να μειώσει τον κίνδυνο και να ενισχύσει τον κώδικα.
PeckShield(opens in a new tab) - Εταιρεία ασφάλειας blockchain που προσφέρει προϊόντα και υπηρεσίες για την ασφάλεια, την ιδιωτικότητα και τη χρηστικότητα ολόκληρου του οικοσυστήματος blockchain.
QuantStamp(opens in a new tab) - Υπηρεσία ελέγχου που διευκολύνει την ευρύτερη υιοθέτηση της τεχνολογίας blockchain μέσω υπηρεσιών αξιολόγησης ασφάλειας και κινδύνου.
OpenZeppelin(opens in a new tab) - Εταιρεία ασφάλειας έξυπνων συμβολαίων που παρέχει ελέγχους ασφάλειας για κατανεμημένα συστήματα.
Runtime Verification(opens in a new tab) - Εταιρεία ασφάλειας εξειδικευμένη στην τυπική μοντελοποίηση και επαλήθευση έξυπνων συμβολαίων.
Hacken(opens in a new tab) - Ελεγκτής κυβερνοασφάλειας Web3 που φέρνει την προσέγγιση 360 μοιρών στην ασφάλεια blockchain.
Nethermind(opens in a new tab) - Υπηρεσίες ελέγχου Solidity και Cairo, που διασφαλίζει την ακεραιότητα των έξυπνων συμβολαίων και την ασφάλεια των χρηστών σε όλο το Ethereum και το Starknet.
HashEx(opens in a new tab) - Η HashEx επικεντρώνεται στον έλεγχο blockchain και έξυπνων συμβολαίων για να διασφαλίσει την ασφάλεια των κρυπτονομισμάτων, παρέχοντας υπηρεσίες όπως ανάπτυξη έξυπνων συμβολαίων, δοκιμές διείσδυσης, συμβουλευτική blockchain.
Code4rena(opens in a new tab) - Ανταγωνιστική πλατφόρμα ελέγχου που δίνει κίνητρα στους ειδικούς ασφάλειας έξυπνων συμβολαίων να εντοπίσουν ευπάθειες και να βοηθήσουν να καταστεί το web3 πιο ασφαλές.
CodeHawks(opens in a new tab) - Ανταγωνιστική πλατφόρμα ελέγχου που φιλοξενεί διαγωνισμούς ελέγχου έξυπνων συμβολαίων για ερευνητές ασφάλειας.
Cyfrin(opens in a new tab) - Δυναμικός παράγοντας για την ασφάλεια του Web3, που ανοίγει τον δρόμο της ασφάλειας κρυπτογράφησης μέσω προϊόντων και υπηρεσιών ελέγχου έξυπνων συμβολαίων.
ImmuneBytes(opens in a new tab) - Εταιρεία ασφάλειας Web3 που προσφέρει ελέγχους ασφάλειας για συστήματα blockchain μέσω μιας ομάδας έμπειρων ελεγκτών και κορυφαίων εργαλείων.
Oxorio(opens in a new tab) - Έλεγχοι έξυπνων συμβολαίων και υπηρεσίες ασφάλειας blockchain με εξειδίκευση σε EVM, Solidity, ZK, τεχνολογία διασταύρωσης αλυσίδων για κρυπτο εταιρείες και έργα DeFi.
Inference(opens in a new tab) - Εταιρεία ελέγχου ασφάλειας, εξειδικευμένη στον έλεγχο έξυπνων συμβολαίων για αλυσίδες μπλοκ που βασίζονται σε EVM. Χάρη στους έμπειρους ελεγκτές της, εντοπίζουν πιθανά προβλήματα και προτείνουν εφαρμόσιμες λύσεις για την επίλυσή τους πριν από την ανάπτυξη.
Πλατφόρμες εύρεσης σφαλμάτων
Immunefi(opens in a new tab) - Πλατφόρμα ανταμοιβής για εντοπισμό σφαλμάτων για έξυπνα συμβόλαια και έργα DeFi, όπου οι ερευνητές ασφάλειας εξετάζουν τον κώδικα, αποκαλύπτουν ευπάθειες, πληρώνονται και κάνουν τον κόσμο των κρυπτονομισμάτων πιο ασφαλή.
HackerOne(opens in a new tab) -Πλατφόρμα συντονισμού ευπαθειών και ανταμοιβής για εντοπισμό σφαλμάτων που συνδέει επιχειρήσεις με ερευνητές διείσδυσης και ασφάλειας στον κυβερνοχώρο.
HackenProof(opens in a new tab) - Εξειδικευμένη πλατφόρμα ανταμοιβής για εντοπισμό σφαλμάτων για κρυπτο-έργα (DeFi, Έξυπνα Συμβόλαια, Πορτοφόλια, Κεντρικά Ανταλλακτήρια και άλλα), όπου οι επαγγελματίες ασφάλειας παρέχουν υπηρεσίες διαλογής και οι ερευνητές πληρώνονται για σχετικές, επαληθευμένες αναφορές σφαλμάτων.
Sherlock(opens in a new tab) - Ασφαλιστική στο Web3 για ασφάλεια έξυπνων συμβολαίων, με πληρωμές για ελεγκτές τις οποίες διαχειρίζονται μέσω έξυπνων συμβολαίων για να διασφαλιστεί ότι τα σχετικά σφάλματα πληρώνονται δίκαια.
CodeHawks(opens in a new tab) - Ανταγωνιστική πλατφόρμα ανταμοιβής για την εύρεση σφαλμάτων όπου οι ελεγκτές συμμετέχουν σε διαγωνισμούς και προκλήσεις ασφάλειας και (σύντομα) σε ιδιωτικούς ελέγχους τους.
Δημοσιεύσεις γνωστών ευπαθειών και εκμεταλλεύσεων έξυπνων συμβολαίων
ConsenSys: Γνωστές Επιθέσεις σε Έξυπνα Συμβόλαια(opens in a new tab) - Φιλική προς αρχάριους επεξήγηση των πιο σημαντικών ευπαθειών συμβολαίων, με δείγμα κώδικα για τις περισσότερες περιπτώσεις.
SWC Registry(opens in a new tab) - Επιμελημένη λίστα στοιχείων Common Weakness Enumeration (CWE) που ισχύουν για έξυπνα συμβόλαια Ethereum.
Rekt(opens in a new tab) - Τακτικά επικαιροποιούμενη δημοσίευση υψηλού προφίλ κρυπτο-χακαρίσματος και εκμεταλλεύσεων, που συνοδεύεται από λεπτομερείς εκθέσεις μεταθανάτιου ελέγχου.
Προκλήσεις για την Εκμάθηση της Ασφάλειας Έξυπνων Συμβολαίων
Awesome BlockSec CTF(opens in a new tab) - Επιμελημένη λίστα παιχνιδιών πολέμου ασφάλειας blockchain, προκλήσεων και διαγωνισμών Capture The Flag(opens in a new tab) και λύσεων.
Damn Vulnerable DeFi(opens in a new tab) - Παιχνίδι πολέμου για να διδαχθείτε την επιθετική ασφάλεια των έξυπνων συμβολαίων DeFi και να αναπτύξετε δεξιότητες εντοπισμού σφαλμάτων και ελέγχου ασφάλειας.
Ethernaut(opens in a new tab) - Παιχνίδι πολέμου βασισμένο σε Web3/Solidity όπου κάθε επίπεδο είναι ένα έξυπνο συμβόλαιο που πρέπει να «χακαριστεί».
HackenProof x HackTheBox(opens in a new tab) - Πρόκληση hacking έξυπνων συμβολαίων, που εκτυλίσσεται σε μια φανταστική περιπέτεια. Η επιτυχημένη ολοκλήρωση της πρόκλησης δίνει επίσης πρόσβαση σε ένα ιδιωτικό πρόγραμμα ανταμοιβής για την εύρεση σφαλμάτων.
Βέλτιστες πρακτικές για την ασφάλεια έξυπνων συμβολαίων
ConsenSys: Καλύτερες πρακτικές ασφάλειας έξυπνων συμβολαίων Ethereum(opens in a new tab) - Πλήρης κατάλογος οδηγιών για την ασφάλεια έξυπνων συμβολαίων Ethereum.
Nascent: Simple Security Toolkit(opens in a new tab) - Συλλογή πρακτικών οδηγών και λιστών ελέγχου με επίκεντρο την ασφάλεια για την ανάπτυξη έξυπνων συμβολαίων.
Solidity Patterns(opens in a new tab) - Χρήσιμη συλλογή ασφαλών προτύπων και βέλτιστων πρακτικών για τη γλώσσα προγραμματισμού έξυπνων συμβολαίων Solidity.
Solidity Docs: Security Considerations(opens in a new tab) - Οδηγίες για τη συγγραφή ασφαλών έξυπνων συμβολαίων με Solidity.
Smart Contract Security Verification Standard(opens in a new tab) - Κατάλογος ελέγχου δεκατεσσάρων μερών που δημιουργήθηκε για να τυποποιήσει την ασφάλεια των έξυπνων συμβολαίων για προγραμματιστές, αρχιτέκτονες, ελεγκτές ασφάλειας και προμηθευτές.
**Learn Smart Contract Security and Auditing(opens in a new tab) - Το απόλυτο μάθημα ασφάλειας και ελέγχου έξυπνων συμβολαίων, δημιουργημένο για προγραμματιστές έξυπνων συμβολαίων που θέλουν να αναβαθμίσουν τις βέλτιστες πρακτικές ασφάλειας τους και να γίνουν ερευνητές ασφάλειας.