Table of Contents
Οι υπερχειλίσεις των buffer παραμένουν ένα από τα πιο επίμονα και επικίνδυνα τρωτά σημεία ασφάλειας στον προγραμματισμό C. Παρά το γεγονός ότι είναι καλά τεκμηριωμένα για δεκαετίες, συνεχίζουν να προκαλούν σοβαρά ζητήματα όπως η διαφθορά δεδομένων, καταρρεύσεις συστημάτων, και απομακρυσμένη εκτέλεση κώδικα. Η εγγραφή ασφαλούς κώδικα C απαιτεί μια βαθιά κατανόηση του πώς υπερχείλιση ενδιάμεσης μνήμης συμβαίνουν και μια πειθαρχημένη προσέγγιση για την πρόληψη τους. Αυτό το άρθρο παρέχει έναν περιεκτικό οδηγό για τη συγγραφή στιβαρών, ανθεκτικών στην υπερχείλιση κώδικα C, που καλύπτει θεμελιώδεις έννοιες, ασφαλείς λειτουργίες, τεχνικές επικύρωσης, προστασία μεταγλωττιστών, και σύγχρονα αμυντικά μέτρα.
Κατανόηση των υπερροών του Buffer
Μια υπερχείλιση ενδιάμεσης μνήμης συμβαίνει όταν ένα πρόγραμμα γράφει περισσότερα δεδομένα σε ένα συνεχές μπλοκ μνήμης (ένας ρυθμιστής) από ό, τι είχε κατανεμηθεί για να κρατήσει. Δεδομένου ότι οι απομονωτές κατοικούν στη στοίβα ή τη μνήμη σωρού, υπερβαίνοντας τα όριά τους αντικαθιστά παρακείμενες τοποθεσίες μνήμης. Αυτή η διαφθορά μπορεί να αλλάξει την κατάσταση του προγράμματος, να εισαγάγει απρόβλεπτη συμπεριφορά, ή να χρησιμοποιηθεί από έναν επιτιθέμενο για να εγχύσει και να εκτελέσει αυθαίρετο κώδικα.
Οι συνέπειες εξαρτώνται από το τι γίνεται αντιγραφέα. Αντικατάσταση μιας διεύθυνσης επιστροφής στη στοίβα μπορεί να ανακατευθύνει την εκτέλεση σε επιτιθέμενο-ελέγχον κώδικα. Οι δείκτες αντικατάστασης μπορούν να οδηγήσουν σε αυθαίρετες σημειώσεις μνήμης. Ακόμα και απλές καταρρεύσεις μπορούν να αξιοποιηθούν για επιθέσεις άρνησης της υπηρεσίας. Η κατανόηση της μηχανικής είναι το πρώτο βήμα για την πρόληψη.
Υπερροές βάσει στοίβας
Οι τοπικές μεταβλητές, συμπεριλαμβανομένων των ρυθμιστών που δηλώνονται μέσα στις λειτουργίες, αποθηκεύονται στη στοίβα. Η στοίβα κατέχει επίσης τη διεύθυνση επιστροφής, αποθηκεύονται δείκτες καρέ και άλλα δεδομένα ελέγχου. Όταν μια γραμμική ενδιάμεση μνήμη όπως υπερκαλύπτεται, τα δεδομένα διαρρέουν στη διεύθυνση επιστροφής και πέρα.
Υπερχείλιση βάσει του θερμοστασίου
Δυναμικά κατανεμημένες προσκρουστήρες (μέσω , , κ.λπ.) κατοικούν στο σωρό. Οι υπερχείλιση εδώ μπορούν να διαφθείρουν μεταδεδομένα που χρησιμοποιούνται από τον κατανεμητή, οδηγώντας σε καταρρεύσεις ή εκμετάλλευση μέσω ψεκασμού σωρού ή επιθέσεις μετά τη χρήση. Οι υπερχείλιση των πυκνοκατοικημένων είναι δυσκολότερο να εκμεταλλευτούν αλλά εξίσου επικίνδυνες.
Κοινές Ευάλωτες Λειτουργίες και Ασφαλείς Εναλλακτικές τους
Η C τυπική βιβλιοθήκη παρέχει διάφορες λειτουργίες που δεν εκτελούν έλεγχο ορίων. Χρησιμοποιώντας τους είναι η πιο κοινή αιτία των υπερχειλίσεις ενδιάμεσης μνήμης. Αντικατάσταση τους με ασφαλέστερους ομολόγους είναι μια θεμελιώδης βέλτιστη πρακτική.
Αντίγραφο συμβολοσειράς και Κονκατάσταση
- — Μη ασφαλή: αντίγραφα μέχρι μηδενικού εξολοθρευτή, χωρίς όριο μήκους.
Ασφαλή εναλλακτική λύση:[ ] — αντίγραφα το πολύ n χαρακτήρες· σημειώστε ότι δεν ακυρώνει αν η πηγή είναι μεγαλύτερη από n, οπότε πάντα με το χέρι μηδενική-τερματίζεται. - Ακόμα καλύτερα: — διαθέσιμο σε BSD και πολλά συστήματα Linux· πάντα μηδενίζει και επιστρέφει το μήκος της συμβολοσειράς πηγής για ανίχνευση trunsion.
- — Μη ασφαλή: συμπυκνώματα χωρίς όρια.
Ασφαλή εναλλακτική λύση: — προσαρτήματα το πολύ n χαρακτήρες και πάντοτε μηδενικά.
Μορφοποιημένη έξοδος και είσοδος
- — Μη ασφαλές: γράφει σχηματοποιημένη έξοδο σε ένα ρυθμιστικό διάλυμα χωρίς έλεγχο μεγέθους.
Ασφαλή εναλλακτική λύση:[] — περιορίζει την έξοδο σε χαρακτήρες μεγέθους-1 συν μηδενικό τερματικό. - — Παρόμοιος κίνδυνος· χρήση ] αντ' αυτού.
- — Εξαιρετικά επικίνδυνο· απομακρύνεται από το πρότυπο C11. Χρήση αντ' αυτού.
- — Χωρίς έλεγχο ορίων. Χρήση ή με προσδιοριστικό πλάτος πεδίου.
Αντιγραφή και μετακίνηση μνήμης
- — Ασφαλέστερη εναλλακτική λύση: (επικαλυπτόμενα χειρόγραφα) και πάντα να εξασφαλίζει n ≤ μέγεθος dest.
- Ορισμένες πλατφόρμες παρέχουν [[LFT:19]] από το παράρτημα Κ (προαιρετικό στο C11), αλλά η έγκριση είναι περιορισμένη.
Επικύρωση και Διαχείριση μεγέθους
Ακόμα και με ασφαλείς λειτουργίες, θα πρέπει να επικυρώσετε τα μήκη εισόδου, να εξασφαλίσει κατάλληλα μεγέθη ρυθμιστή, και να χειριστεί το δυναμικό trunsion με χάρη.
Έλεγχος μήκους εισόδου
Πριν την αντιγραφή ή την επεξεργασία εξωτερικών εισροών (εισροή χρήστη, δεδομένα δικτύου, περιεχόμενο αρχείου), προσδιορίστε το μέγιστο αποδεκτό μήκος του και απορρίψτε ή περιορίσετε τα δεδομένα που το υπερβαίνουν. Για παράδειγμα:
#define MAX_INPUT 255
char buffer[MAX_INPUT + 1]; // +1 for null
if (strlen(user_input) > MAX_INPUT) {
// Handle error: reject or truncate
fputs("Input too long", stderr);
return -1;
}
strncpy(buffer, user_input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
Χρήση προσαρμογών σταθερού μεγέθους με γνωστά όρια
Όποτε είναι δυνατόν, ορίστε τις προσκρουστήρες με σταθερό μέγεθος και επιβάλετέ τις σε όλο τον κώδικα. Αποφύγετε τις συστοιχίες μεταβλητού μήκους (VLAs) που μπορούν να προκαλέσουν υπερχείλιση στοίβας αν παρέχονται μεγάλα μεγέθη. Αντ 'αυτού, κατανέμετε δυναμικά με σαφείς ελέγχους μεγέθους.
Χειρισμός της τριβής με ακρίβεια
Λειτουργίες όπως [[LFT:21]] και [[LFT:22]] μπορούν να διαγράψουν δεδομένα. Να γνωρίζετε την τιμή επιστροφής για να ανιχνεύσετε την περικοπή και να αποφασίσετε αν τα κατατεθειμένα δεδομένα είναι αποδεκτά ή αν θα πρέπει να προκύψει σφάλμα. Η άγνοια της περικοπής μπορεί να αφήσει τα ρυθμιστικά ρυθμιστικά στελέχη σε μη αναμενόμενη κατάσταση.
Σημαίες Ασφαλείας Μεταγλωττιστών και Προστασία Χρόνου Εκτέλεσης
Οι σύγχρονοι μεταγλωττιστές προσφέρουν σημαίες που προσθέτουν ανίχνευση υπερχείλισης και μετριασμός χωρίς αλλαγές κώδικα. Ενεργοποιήστε τις στο σύστημα κατασκευής σας.
- / [ — Εισάγει στοίβα καναρίνια (τιμές τύχης) πριν από τις διευθύνσεις επιστροφής. Αν μια ενδιάμεση υπερχείλιση αντικαταστήσει το καναρίνι πριν τροποποιήσει τη διεύθυνση επιστροφής, το πρόγραμμα ματαιώνεται πριν ολοκληρωθεί η εκμετάλλευση.
- — Αντικαθιστά τις κλήσεις σε μη ασφαλείς λειτουργίες όπως και με ελεγμένες εκδόσεις που ματαιώνουν αν ο ρυθμιστής προορισμού είναι πολύ μικρός. Απαιτεί ] ή υψηλότερη βελτιστοποίηση.
- — Προειδοποιεί για τις δυσαρέστητες συμβολοσειρές μορφής που μπορούν να οδηγήσουν σε υπερχείλιση ρυθμιστικού αποθέματος ή σε διαρροές πληροφοριών.
- — Κωδικός οργάνων AddressSanitizer (ASan) για τον εντοπισμό υπερχειλιστικών ζωνών, μη χρήσης και άλλων σφαλμάτων μνήμης κατά τη διάρκεια της εκτέλεσης. Επιβραδύνει την εκτέλεση αλλά είναι ανεκτίμητη για δοκιμές.
- — Αποφεύγει τους ελέγχους υπερχείλισης (χρησιμοποιείται με προσοχή).
Προστασία λειτουργικού συστήματος
Οι τεχνολογίες μετριασμού της εκμετάλλευσής τους στα σύγχρονα λειτουργικά συστήματα περιλαμβάνουν:
- Προληπτική εκτέλεση δεδομένων (DEP) / NX bit — Στοίβα σημάτων και σωρών ως μη εκτελέσιμων, εμποδίζοντας την εκτέλεση του κώδικα κελύφους.
- Διαστημική Διάταξη Διεύθυνσης Τυχαία (ASLR) — Τυχαίνει διευθύνσεις μνήμης (στακ, σωρός, κοινόχρηστες βιβλιοθήκες) για να καταστήσει δυσκολότερη την πρόβλεψη στόχων.
- Αναδιανομή μόνο (RELRO) — Προστατευτικά GOT (Global Offset Table) από την αντικατάσταση.
Η ενεργοποίηση αυτών των προστατευμών (συνήθως προεπιλεγμένη) αυξάνει τη γραμμή για εκμετάλλευση αλλά δεν αντικαθιστά την ασφαλή κωδικοποίηση.
Έλεγχοι κώδικα και Στατική Ανάλυση
Η ανθρώπινη αναθεώρηση σε συνδυασμό με αυτοματοποιημένη στατική ανάλυση μπορεί να πιάσει τα θέματα της υπερχείλισης ενδιάμεσης μνήμης νωρίς.
- Εγχειρίδιο αναθεώρηση κώδικα — Αναζητήστε χρήσεις μη ασφαλών λειτουργιών, ελλείποντες έλεγχοι μεγέθους και βρόχοι που γράφουν πέρα από τα όρια των προσθέτων.
- Στατικά εργαλεία ανάλυσης — Εργαλεία όπως , , και ανιχνεύουν πιθανές υπερχειλίσεις, χρήση επικίνδυνων λειτουργιών και λάθη εκτός λειτουργίας.
- Fuzzing[[LIT:1]] — Χρησιμοποιήστε το libFuzzer, AFL ή άλλα fuzzers για να ελέγξετε αυτόματα τον χειρισμό εισόδου με απροσδόκητα δεδομένα που μπορεί να προκαλέσουν υπερχείλιση.
Πρακτικά Παραδείγματα Ασφαλούς Κώδικα
Ασφαλές αντίγραφο συμβολοσειρών με έλεγχο ορίων
#include <stdio.h>
#include <string.h>
int safe_string_copy(char *dest, size_t dest_size, const char *src) {
if (!dest || !src || dest_size == 0) {
return -1; // Invalid parameters
}
size_t src_len = strlen(src);
if (src_len >= dest_size) {
// Source too large; truncation or error
// Option: copy what fits and null-terminate
strncpy(dest, src, dest_size - 1);
dest[dest_size - 1] = '\0';
return 1; // Truncation occurred
}
strncpy(dest, src, dest_size);
// strncpy fills remaining with null, so dest_size fits; no need to null-terminate if src shorter
return 0; // Success, no truncation
}
Ασφαλής ακέραιος χειρισμός για τα μεγέθη των αντισταθμιστών
Η υπερχείλιση του buffer μπορεί επίσης να προκύψει από ακέραιες υπερχειλίσεις όταν τα μεγέθη υπολογιστών. Πάντα ελέγξτε αριθμητική πριν από την κατανομή.
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
void *safe_malloc_array(size_t nmemb, size_t size) {
if (nmemb == 0 || size == 0) {
return NULL; // Or handle zero-size allocation
}
if (nmemb > SIZE_MAX / size) {
// Integer overflow would occur
errno = ENOMEM;
return NULL;
}
return malloc(nmemb * size);
}
Χρήση snprintf για μορφοποιημένες συμβολοσειρές
char log_message[256];
int ret = snprintf(log_message, sizeof(log_message),
"User %s logged in from %s", username, ip_address);
if (ret < 0) {
// Output error
} else if ((size_t)ret >= sizeof(log_message)) {
// Truncation occurred; handle if needed
}
Πρόσθετες Βέλτιστες Πρακτικές
- Έναρξη προσκρουστήρων — Πάντα μηδενικά-αρχικά ρυθμιστικά για να αποφευχθεί η διαρροή μη αρχικώς ενεργοποιημένης μνήμης.
- Αποφύγετε την αναδρομή με αδέσμευτο βάθος[[LFT:1]] — Οι υπερχείλισεις στοιβάδων μπορούν να συμβούν από βαθιά αναδρομή· χρησιμοποιήστε επανάληψη ή οριακή βάθος.
- Χρήση προκριματικού — Βοηθά τον μεταγλωττιστή να βελτιστοποιήσει και μπορεί να πιάσει θέματα ψευδωνύμου, αν και δεν εμποδίζει άμεσα τις υπερχειλίσεις.
- Προτιμήστε -ορθότητα — Αποτρέπει την τυχαία τροποποίηση συμβολοσειρών εισόδου και επιβάλλει πρόθεση.
- Εφαρμογή του χειρισμού σφαλμάτων — Μην αγνοείτε τις τιμές επιστροφής από λειτουργίες όπως , , , κ.λπ.
Πόροι για περαιτέρω μάθηση
- SEI CERT C Coding Standard — Ολοκληρωμένοι κανόνες για ασφαλή κωδικοποίηση C.
- CWE-120: Αντίγραφο αντιγράφων χωρίς έλεγχο μεγέθους εισόδου — ταξινόμηση του MITRE για αδυναμίες υπερχείλισης του αποθέματος ασφαλείας.
- ΟΥΑΣΠ Buffer Overflow — Πρακτική καθοδήγηση από το Σχέδιο Ασφαλείας Ανοιχτών Εφαρμογών Ιστού.
- GNU C Εγχειρίδιο Βιβλιοθήκης: Βοηθητικά προγράμματα εγχόρδων και διατάξεων — Τεκμηρίωση για ασφαλείς λειτουργίες χορδών.
- ΔιεύθυνσηSanitizer — Ένας ανιχνευτής σφαλμάτων γρήγορης μνήμης.
Συμπέρασμα
Η πρόληψη των υπερχειλήσεων του ρυθμιστικού αποθέματος σε C δεν είναι προαιρετική, είναι θεμελιώδης ευθύνη κάθε προγραμματιστή που εργάζεται με τη γλώσσα. Κατανοώντας τους μηχανισμούς των υπερχειλήσεων, αντικαθιστώντας επικίνδυνες λειτουργίες με ασφαλέστερες εναλλακτικές, αυστηρά επικυρώνοντας εισροές και μεγέθη, επιτρέποντας προστασία μεταγλωττιστή, και χρησιμοποιώντας στατική ανάλυση και δοκιμές, μπορείτε να μειώσετε δραματικά τον κίνδυνο αυτών των τρωτών σημείων. Καμία ενιαία τεχνική δεν είναι επαρκής, άμυνα σε βάθος — συνδυάζοντας πειθαρχία κωδικοποίησης, σημαίες μεταγλωττιστών, μετριασμό OS, και ενδελεχή δοκιμή ⁇ παρέχει την ισχυρότερη προστασία. Με αυτές τις πρακτικές, μπορείτε να γράψετε C κώδικα που είναι τόσο ισχυρός και ασφαλής, ικανός να τρέχει με ασφάλεια σε κρίσιμα συστήματα. Θυμηθείτε: ασφαλής κωδικοποίηση είναι μια συνεχής πρακτική, όχι μια ενιαία διόρθωση. Μείνετε ενημερωμένοι για νέες ευπαθείς και συνεχώς αναθεώρηση και βελτίωση του κώδικα σας.