Domanda

  

"L'uomo medio non vuole essere libero. Vuole semplicemente per essere sicuri." - H. L. Menken

Sto tentando di scrivere molto sicuro C. Di seguito elenco alcune delle tecniche che uso e chiedo Sono sicuro come penso che siano. Per favore, non non esitate a strappare il mio codice / preconcetti a brandelli. Qualsiasi risposta che trova anche la vulnerabilità più banale o me una nuova idea sarà molto apprezzato insegna .

La lettura da un flusso:

Secondo il GNU C Programming Tutorial getline:

  

La funzione getline sarà   ingrandire automaticamente il blocco di   memoria, come necessario, tramite il realloc   la funzione, quindi non c'è mai una carenza   di spazio - uno dei motivi per cui è getline   così sicuro. [..] Si noti che getline può   tranquillamente gestire la vostra linea di ingresso, nessun   importa quanto tempo ci sia.

Si considera che getline voglia, sotto tutti gli ingressi , impediscono un si verifichi durante la lettura da un flusso.

  • è la mia ipotesi è corretta? ci sono ingressi e / o sistemi di assegnazione ai quali questo potrebbe portare ad un exploit? Per esempio cosa succede se il primo carattere dal flusso è alcuni href="http:///seq/annotation/workspaces/heilman/gff2/f11/M73LQ_4.gff2" bizzarro , forse 0x08 BACKSPACE (CTL-H).
  • È stato un lavoro fatto per dimostrare matematicamente getline il più sicuro?

Malloc restituisce NULL in caso di errore:

Se malloc verifica un errore di malloc restituisce un puntatore NULL. Questo presenta un rischio per la sicurezza dal momento che si può ancora applicare l'aritmetica dei puntatori a un (0x0) puntatore NULL, quindi wikipedia raccomanda

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

sscanf Sicuro:

Quando si utilizza sscanf ho ottenuto l'abitudine di assegnare le dimensioni a stringhe -essere-estratta alla dimensione della stringa di input spera evitando la possibilità di superamento. Ad esempio:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

Poiché str1 e str2 sono le dimensioni della InputStr e altri caratteri di strlen (InputStr) può essere letto da InputStr, sembra impossibile, dato tutti i valori possibili per l'InputStr per causare un buffer troppo pieno?

  • Ho ragione? ci sono strani casi d'angolo non ho pensato?
  • ci sono modi migliori per scrivere questo? Le biblioteche che hanno già risolto vero?

Domande generali:

Mentre io ho postato un gran numero di domande non mi aspetto nessuno a rispondere a tutti loro. Le domande sono più di linea guida per i tipi di risposte che sto cercando. Ho molta voglia di imparare la mentalità sicuro C.

  • Quali altri idiomi sicuro C sono là fuori?
  • Quali casi angolo ho bisogno per controllare sempre
  • Come posso scrivere unit test per far rispettare queste regole?
  • Come faccio a far rispettare i vincoli in un verificabilità o modo dimostrabilmente corretto?
  • Alcune tecniche consigliate statico / dinamico di analisi o strumenti per C?
  • Quali pratiche C sicuri segui e come si fa a giustificare a se stessi e gli altri?

Risorse:

Molte delle risorse sono stati presi in prestito dalle risposte.

È stato utile?

Soluzione

  1. La lettura da un flusso

Il fatto che getline() "allargherà automaticamente il blocco di memoria, come necessario" significa che questo potrebbe essere usato come un attacco di negazione del servizio, come sarebbe banale per generare un ingresso che era così tempo avrebbe esaurire la la memoria disponibile per il processo (o peggio, il sistema!). Una volta che si verifica una condizione di memoria insufficiente, altre vulnerabilità possono anche entrare in gioco. Il comportamento di codice in bassa / senza memoria è raramente bello, e molto difficile da prevedere. IMHO è più sicuro di impostare limiti superiori ragionevoli su tutto, soprattutto nelle applicazioni sensibili alla sicurezza.

Inoltre (come si prevede menzionando caratteri speciali), getline() si dà solo un buffer; non fa alcuna garanzia sul contenuto del buffer (come la sicurezza è interamente dipendente dall'applicazione). Così sanificazione l'ingresso è ancora una parte essenziale di elaborazione e la convalida dei dati degli utenti.

  1. sscanf

Vorrei tendono a preferire di usare una libreria di espressione regolare, e sono espressioni regolari molto strettamente definiti per i dati utente, piuttosto che l'uso sscanf. In questo modo è possibile eseguire una buona dose di convalida al momento dell'ingresso.

  1. Osservazioni generali

    • strumenti di fuzzing sono disponibili che generano ingresso casuale (sia valido e non valido) che può essere utilizzato per testare il vostro input manipolazione
    • Buffermanagement è critica: buffer overflow, underflow, out-of-memoria
    • Condizioni di gara possono essere sfruttati nel codice altrimenti sicuro
    • I file binari possono essere manipolati per iniettare valori non validi o valori di grandi dimensioni nelle intestazioni, quindi codice del formato del file deve essere solida e non assumere dati binari è valida
    • I file temporanei spesso può essere una fonte di problemi di sicurezza, e deve essere gestito con attenzione
    • iniezione di codice può essere utilizzato per sostituire le chiamate di sistema o di libreria runtime con le versioni maligni
    • Plugin forniscono una grande vettore di attacco
    • Come principio generale, suggerirei avere interfacce chiaramente definite in cui i dati dell'utente (o qualsiasi dato dall'esterno dell'applicazione) si presume valido e ostile fino alla trasformazione, sterilizzata e convalidato, e l'unico modo per i dati utente di inserire l'applicazione

Altri suggerimenti

Credo che il vostro esempio sscanf è sbagliato. Si può ancora traboccare quando usato in questo modo.

Prova questo, che specifica il numero massimo di byte da leggere:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

Date un'occhiata a questo articolo dev IBM circa la protezione dai buffer overflow.

In termini di test, vorrei scrivere un programma che genera stringhe casuali di correre e di dar loro da mangiare al vostro programma, e assicurarsi che vengano gestiti correttamente.

Un buon posto per cominciare a guardare a questo è di David Wheeler eccellente sicuro sito di codifica .

Il suo libero servizio di prenotazione online " programmazione sicura per Linux e Unix HOWTO " è un ottimo risorsa che viene regolarmente aggiornato.

Ti potrebbe piacere anche a guardare il suo analizzatore statico eccellente FLAWFINDER per ottenere alcuni ulteriori suggerimenti. Ma ricordate, nessun strumento automatico è un sostituto per un buon paio di occhi esperti, o come David così ricche di colori mette ..

  

Qualsiasi strumento di analisi statica, come FLAWFINDER, è solo uno strumento. Nessuno strumento può sostituire il pensiero umano! In breve, "un pazzo con un attrezzo è ancora uno sciocco" . E 'un errore pensare che gli strumenti di analisi (come FLAWFINDER) sono un sostituto per la formazione sulla sicurezza e la conoscenza

Personalmente ho utilizzato le risorse di David per diversi anni e trovare loro di essere eccellente.

Yannick Moy ha sviluppato un sistema di pre-condizione più debole Hoare / Floyd per C durante il suo dottorato e applicò al CERT gestito stringhe biblioteca . Ha trovato una serie di bug (vedi pagina 197 del suo libro di memorie). La buona notizia è che la biblioteca è più sicuro ora per il suo lavoro.

Si potrebbe anche guardare il sito web di Les Hatton qui e al suo libro Safer C che si può ottenere da Amazon.

Non utilizzare gets() per l'input, l'uso fgets(). Per utilizzare fgets(), se il buffer è allocato automaticamente (vale a dire, "in pila"), quindi utilizzare questo linguaggio:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

In questo modo continuare a lavorare se si decide di modificare la dimensione del buf. Io preferisco questo modulo per:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

perché la prima forma utilizza buf per determinare il secondo argomento, ed è più chiara.


controllare il valore di ritorno di fclose().


Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top