Puskurin ylivirrat ovat edelleen yksi C-ohjelmoinnin sitkeimmistä ja vaarallisimmista tietoturvan haavoittuvuuksista. Vaikka niitä on dokumentoitu hyvin vuosikymmeniä, ne aiheuttavat edelleen vakavia ongelmia, kuten datakorruptiota, järjestelmä kaatuu ja etäkoodin suoritusta. Kirjoittaminen vaatii syvän ymmärryksen siitä, miten puskurin ylivuotoja esiintyy ja kurinalaisen lähestymistavan niiden estämiseksi. Tämä artikkeli tarjoaa kattavan oppaan, jonka avulla voidaan kirjoittaa vankkaa, ylivuotoa kestävä C-koodia, joka kattaa peruskäsitteet, turvalliset toiminnot, validointitekniikat, kääntäjän suojaukset ja nykyaikaiset puolustustoimenpiteet.

Puskurin ylivuotojen ymmärtäminen

Puskurin ylivuoto tapahtuu, kun ohjelma kirjoittaa enemmän tietoa viereiseen muistilohkoon (puskuri) kuin puskuri oli varattu säilytettäväksi. Koska puskurit asuvat pinossa tai kasamuistissa, ylittäen niiden rajat ylittää viereiset muistin sijainnit. Tämä korruptio voi muuttaa ohjelman tilaa, ottaa käyttöön arvaamattoman käyttäytymisen, tai käyttää hyökkääjä ruiskuttaa ja suorittaa mielivaltaisia koodia.

Seuraukset riippuvat siitä, mitä saa ylikirjoitettu. Ylikirjoitus palautusosoite pino voi ohjata teloituksen hyökkääjä-ohjattu koodi. Ylikirjoitus osoittimet voivat johtaa mielivaltaisia muistikirjoituksia. Jopa yksinkertaisia kaatumisia voidaan käyttää hyväksi kieltäytymiseen-of-service hyökkäyksiä. Ymmärtäminen mekaniikka on ensimmäinen askel ennaltaehkäisyyn.

Pinottu ylivirta

Paikalliset muuttujat, mukaan lukien sisällä ilmoitetut puskurit, tallennetaan pinoon. Pinossa on myös palautusosoite, tallennetut kehysosoittimet ja muut ohjaustiedot. Kun lineaarinen puskuri, kuten [], ylitetään, data leviää palautusosoitteeseen ja sen jälkeen. Klassinen hyväksikäyttö, kuten Morris mato (1988), käytti pino ylivirtaa saadakseen luvatonta pääsyä.

Heap-perustunut ylivirta

Dynaamisen jakamat puskurit ([], ] jne.) asuvat kasassa. Ylivirrat täällä voivat korruptoida allokaattorin käyttämää metaa, mikä johtaa kaatumiin tai hyödyntämiseen kasaruiskutuksen tai käytön jälkeen vapaan hyökkäyksen kautta.

Yhteiset haavoittuvuustoiminnot ja niiden turvalliset vaihtoehdot

C-standardikirjasto tarjoaa useita toimintoja, jotka eivät suorita rajatarkastuksia. Niiden käyttö on yleisin syy puskurin ylivuotoihin. Niiden korvaaminen turvallisempiin vastakkaisiin on perustava paras käytäntö.

Merkkijonon kopiointi ja konkaktiointi

  • [][] ... Unsafe: copy with a noll terminator, no pitch limit.
    []............................................................................................................................................................................................
  • Parempi vielä: ... saatavilla BSD:ssä ja monissa Linux-järjestelmissä; aina nollaa ja palauttaa lähdejonon pituuden katkaisun havaitsemiseen.
  • [][] . Unsafe: concatentes without borders.
    [].

Muotoiltu tuloste ja syöte

  • [][] ... Unsafe: kirjoittaa muotoillun lähdön puskuriin ilman koon tarkistusta. [
    [].
  • [[LLT:0]][[LLT:101][[[LLT:1]] ] . .
  • [] .
  • [[] . . Ei rajoja. Käytä tai kenttäleveyden määritintä.

Muistin kopioi ja siirrä

  • [] . Turvallinen vain, jos n todennetaan, ettei määräpaikkapuskurin koko ylitä määrää. [
    []Safer-vaihtoehto:[] [[]] (kahvat päällekkäiset) ja varmista aina n ≤ jälkikoko.
  • Jotkin alustat tarjoavat liitteestä K (valinnainen kohdassa C11), mutta niiden hyväksyminen on vähäistä.

Validointi ja koon hallinta

Turvallisista toiminnoista huolimatta sinun on validoitava syöttöpituudet, varmistettava asianmukaiset puskurikoot ja käsiteltävä mahdollinen karsinta hienovaraisesti.

Tarkista syötepituudet

Ennen ulkoisen syötteen (käyttäjän tulo, verkkotiedot, tiedoston sisältö) kopiointia tai käsittelyä on määritettävä sen suurin sallittu pituus ja hylättävä tai katkaistava sitä suuremmat tiedot. Esimerkiksi:

#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';

Käytä Fixed-kokoisia puskureita tunnetuilla rajoilla

Määrittele puskurit, joiden koko on vakio, ja aseta ne koko koodin käyttöön. Vältä muuttuvia tasoja (VLA), jotka voivat aiheuttaa pino-ylivirtauksia, jos niitä toimitetaan suuria kokoja.

Käsinkäyttö

ja voivat poistaa tietoja. Tiedosta palautusarvo, jotta voidaan havaita katkos, ja päätä, onko katkaistu tieto hyväksyttävä vai pitäisikö virhe nostaa. Katkaisun huomiotta jättäminen voi jättää puskurit odottamattomaan tilaan.

Koostajan turvaliput ja Runtime Protections

Nykyaikaiset kääntäjät tarjoavat liput, jotka lisäävät puskurin ylivuototunnistuksen ja lieventämisen ilman koodimuutoksia. Ota ne käyttöön rakennusjärjestelmässäsi.

  • [[]] / [ ] . . Lisää pinokanarialintuja (satunnaisuusarvot) ennen paluuosoitteita. Jos puskuri ylittää kanarian kielen ennen paluuosoitteen muuttamista, ohjelma keskeyttää ennen kuin se päättyy.
  • [[] . . Korvaa puhelut turvallisiin toimintoihin, kuten ja [.].
  • [[] ... ..................................................................................................................................................................................................................................
  • [[] ... OsoiteSanitizer (ASAn) -instrumenttikoodi puskurin ylivuotojen, käytön jälkeen vapaana olevien ja muiden muistivirheiden havaitsemiseksi ajon aikana. Hidastaa suoritusta, mutta on korvaamaton testauksessa.
  • [[ ] . Välttää optimoimasta ylivuototarkastuksia (käyttäkää varoen).

Käyttöjärjestelmän suojaukset

Pino kanarialinnut ovat vain yksi kerros. Hyödynnä hillitsemistekniikoita modernin käyttöjärjestelmän sisältävät:

  • Tietojen toteutuksen ehkäisy (DEP) / NX bit ...
  • Address Space Layout Satunnaistaminen (ASLR)[ ... Satunnainen muistiosoitteita (pino, kasa, jaettu kirjastot) tehdä vaikeammin ennustaa tavoitteita.
  • Siirtopaikka Read-Only (RELRO) .

Näiden suojainten (yleensä oletus) mahdollistaminen nostaa riistoa, mutta ei korvaa turvallista koodausta.

Kooditarkastukset ja staattinen analyysi

Ihmisen arvio yhdistettynä automaattiseen staattiseen analyysiin voi tarttua puskurin ylivuoto-ongelmaan aikaisin. Integroi nämä kehitystyöhön.

  • Manuaalinen koodin katsaus[ ... ... Etsi käyttöjä, jotka eivät ole turvallisia toimintoja, puuttuvat koon tarkistukset ja silmukat, jotka kirjoitetaan puskurirajojen yli.
  • ] Staattiset analyysityökalut[ . Työkalut, kuten . . .
  • Fuzng ... Käytä libFuzzeria, AFL:ää tai muita Fuzzerseja automaattisesti testataksesi syötteen käsittelyä odottamattomilla tiedoilla, jotka voivat aiheuttaa ylivirtaa.

Käytännön esimerkkejä turvakoodista

Turvallinen merkkijonokopio, jossa reunat tarkastetaan

#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
}

Turvallinen puskurikokojen integer-käsittely

Puskurin ylivuoto voi myös johtua kokonaisluku ylivirtaa kun laskenta koot. Tarkista aina aritmeettinen ennen jakoa.

#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);
}

Käyttämällä snprintf for Formated Strings

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
}

Parhaiden käytäntöjen lisääminen

  • Aloita puskurit[ . ... aina nolla-alusta puskurit, jotta vältytään vuotamasta alustamatonta muistia.
  • Vältä rekursiota rajaamattomalla syvyydellä[ ... ................................................................................................................................................................................................................................
  • Käytä karsintaa[ .
  • Peru ]-korjattavuus[ .
  • ] Täydennyksen virhekäsittely[ . ... älä jätä huomiotta palautusarvoja sellaisista toiminnoista kuin . . .

Lisäoppimiseen tarkoitetut resurssit

Päätelmät

Estämällä puskurin ylivuotoja C:ssä ei ole valinnainen; se on perusvastuu kenen tahansa kehittäjän työstää kieltä. Ymmärtämällä mekanismeja ylivirtaa, korvaamalla vaarallisia toimintoja turvallisempien vaihtoehtojen kanssa, tarkasti validoimalla syötteitä ja kokoja, mahdollistamalla kääntäjän suojaukset, ja käyttämällä staattista analyysiä ja testausta, voit dramaattisesti vähentää riskiä näiden haavoittuvuuksien. Yksittäinen tekniikka ei riitä; puolustus syvällisesti . Yhdistämällä koodauskuria, kääntäjän liput, käyttöjärjestelmän hallinta ja perusteellinen testaus . Näillä käytännöillä voit kirjoittaa C-koodia, joka on sekä tehokas että turvallinen, pystyy toimimaan turvallisesti kriittisissä järjestelmissä. Muista: turvallinen koodaus on jatkuva käytäntö, ei kertaluonteinen. Pysy ajan tasalla uusista haavoittuvuuksista ja jatkuvasti tarkistaa ja parantaa koodia.