Quali elementi dovrei controllare quando si dà un controllo client del parametro filename alla funzione open_C di C?

4

Sto facendo un progetto scolastico dove stiamo costruendo un semplice web server in C. Per implementarlo leggo la prima riga della richiesta (tutto ciò di cui ho bisogno per i miei scopi) e analizzo la stringa centrale come il nome del file usando %codice%. Quindi una richiesta tipica sarebbe qualcosa del tipo:

GET / HTTP/1.0

e il mio codice di risposta sarebbe qualcosa del tipo:

  // first token is request type
  token = strtok(buffer, " \n\r");
  if(strcmp(token, "GET") != 0) {
    // sendResponse just sends a response to the client
    // with the appropriate headers
    sendResponse(connection, -1, HTTP_11_400, strlen(HTTP_11_400), NULL);
    break;
  }
  char *request_type = token;

  // second token is url
  token = strtok(NULL, " \n\r");
  if(strstr(token, "../") != NULL) {
    sendResponse(connection, -1, HTTP_11_403, strlen(HTTP_11_403), NULL);
    break;
  }
  char *request_url = token;

  // third token is HTTP protocol
  token = strtok(NULL, " \n\r");
  if(strcmp(token, "HTTP/1.0") != 0 && strcmp(token, "HTTP/1.1") != 0) {
    sendResponse(connection, -1, HTTP_11_400, strlen(HTTP_11_400), NULL);
    break;
  }
  char *protocol = token;

  printf("filename: %s\n", request_url);
  if(strcmp(request_url, "/") == 0) {
    request_url = "index.html";
  }
  if(*request_url == '/') {
    request_url = request_url++;
  }
  char* fileType = strrchr(request_url, '.') + sizeof(char);
  fd = open(request_url, O_RDONLY);

Come puoi vedere, ho già verificato che il client non sia in grado di risalire una directory. Guardando il mio codice, sto fondamentalmente mettendo una stringa immessa dall'utente e confidando nell'utente, ci sono altre caratteristiche di sicurezza che dovrei prendere in considerazione? (Devo anche scrivere un articolo su questa implementazione e penso che sarebbe bello scrivere sulla sicurezza).

    
posta Mike Flynn 27.02.2014 - 00:32
fonte

4 risposte

2

Questa è una domanda apparentemente semplice in quanto si riduce a come si possono abusare dei percorsi. Io per primo non pretendo di sapere in quanti modi possono essere abusati perché ce ne sono molti. Alcuni di questi sono specifici per piattaforma e non è stata menzionata alcuna piattaforma. Non stai eseguendo alcuna corrispondenza dei percorsi per il controllo degli accessi, trasferendo il controllo a gestori diversi in base al contenuto del percorso o alla codifica percentuale della decodifica, quindi è un po 'più semplice.

Il problema principale che vedo con il tuo codice è il controllo incompleto per un percorso assoluto. Il desiderio sembra essere che verranno restituiti solo i file nella directory corrente e quindi lo stripping di una barra iniziale. Questo è incompleto anche se l'avvio del percorso con più di una barra consentirà a un utente malintenzionato di richiedere file al di fuori della directory corrente (ad esempio //etc/passwd ). È possibile risolvere questo problema eliminando qualsiasi numero di barre iniziali o preppendendo il percorso root del server al percorso della richiesta. O lo farebbe. Entrambi potrebbero essere saggi.

Dopo i controlli di apertura e di errore, dovresti% s_del% per fare un po 'di sanità mentale, controllando che ciò che hai appena aperto è solo un semplice file e una cauzione in caso contrario. Sebbene non sia probabile che si trovi nella root del server, probabilmente non vorrai leggere da un file di dispositivo. Inoltre alcune piattaforme (di nuovo, non specificate nella domanda) restituiranno felicemente un flusso di strutture fstat con una semplice apertura di una directory che fornisce gli elenchi della directory degli attaccanti.

Devi conoscere le potenziali stranezze della piattaforma su cui ti trovi (il tuo codice sembra essere posix ma potrebbe essere un livello di libreria come cygwin). Se sei abbastanza sfortunato da gestire tali finestre, devi considerare problemi come se vuoi consentire l'accesso a flussi di file alternativi (suffisso dirent ). In particolare, devi assicurarti di non consentire all'utente malintenzionato di accedere ai nomi dei dispositivi dos ( link ).

Un ultimo bocconcino pedante. :stream non è thread-safe. Non si specifica nella domanda se si trattasse di un server multi-thread o meno. Se è così, hai una condizione di gara. Anche se non fosse multi-thread, cambierei il tuo codice per usare strtok . Una volta ho visto una mod di apache scritta per la 1.3 che faceva eseguire il porting ai worker preforked su 2.x, che di solito utilizza thread che utilizzavano strtok_r . Era la soluzione perfetta per 1.3 ma i portatori non si sono accorti del problema al momento del porting.

    
risposta data 27.02.2014 - 04:04
fonte
1

Il minimo che devi fare è assicurarti che .. non sia mai usato come componente del percorso. Basta controllare che:

  • Il percorso richiesto inizia con / . (In caso contrario, potresti voler rifiutarlo o forzare prima / prima di fare il prossimo controllo.)
  • Il percorso richiesto non contiene la sottostringa /../ .
  • Il percorso richiesto non termina con il suffisso /.. .

Il tuo controllo non è completamente corretto: lascia /.. a.

Prepara la radice web al percorso fornito dall'utente (ricorda che deve iniziare con / ). La web root dovrebbe essere un percorso assoluto (in un programma multithreading, spesso non puoi contare sulla directory corrente).

Questo è sufficiente per confinare i client alla web root. Nota che se ci sono collegamenti simbolici nella web root, saranno seguiti. Assicurati di non avere file speciali (named pipe, device device, ecc.) Sotto la web root.

Se esegui il controllo degli accessi, tieni presente che potrebbero esserci più percorsi che si riferiscono allo stesso file, per vari motivi, tra cui:

  • // equivale a /
  • /./ equivale a /
  • link simbolici
  • alcuni filesystem equivalgono a caratteri, ad es. A = a su file system senza distinzione tra maiuscole e minuscole
  • il significato dei caratteri non ASCII può essere diverso nella richiesta e nel file system in quanto dipende dalla codifica dei caratteri.

Se si converte la richiesta dalla codifica URL (con % -esoli escape esadecimali) in una stringa non elaborata, si noti che in tale fase potrebbero apparire / e . caratteri. I controlli del percorso devono essere eseguiti sulla stringa finale che verrà utilizzata come frammento del percorso.

La mia risposta presuppone Linux. Windows è più pericoloso a causa dei suoi nomi di file speciali (vedi la risposta di Graham ).

    
risposta data 27.02.2014 - 04:27
fonte
0

Avvicinalo come una mappatura da "ciò che l'utente dovrebbe essere autorizzato a fare" ai "file sul server".

Controllare che non possano "salire" una directory è un esempio di limitazione della mappatura: presuppone che la configurazione del server abbia designato una cartella "sicura", in modo tale che tutti i file in quella cartella, in modo ricorsivo, siano sicuri.

Tutto il resto dipende molto dalla tua situazione. Apache, per esempio, usa un file chiamato .htaccess per controllare i permessi. Di conseguenza, Apache deve garantire che l'utente finale non chieda direttamente una copia di .htaccess (che gli dirà le autorizzazioni su tutte le altre cartelle). Come un altro esempio sulle versioni Apache basate su unix, apache controlla anche che il file richiesto non sia un soft-link per filesystem su un file al di fuori della cartella "safe".

    
risposta data 27.02.2014 - 03:32
fonte
0

C è un linguaggio difficile per fare manipolazioni di stringhe legate alla sicurezza. Ma presumibilmente non hai scelta.

Se devi supportare unicode, ti consiglio di utilizzare una libreria appropriata per questo. Altrimenti (visto che si tratta di un progetto scolastico) suggerirei semplicemente di rifiutare qualsiasi richiesta che non sia limitata a ASCII a 7 bit. Quindi questo è (uchar) c > 127 per qualsiasi carattere nella richiesta.

Potresti anche voler escludere qualsiasi personaggio che possa rappresentare un affare sciocco. Se sai che tutti i tuoi file sono costituiti da A-Za-z0-9._- , quindi rifiuta qualsiasi richiesta che non segue questo modello. La cosa migliore è la lista bianca piuttosto che la lista nera. Questo passaggio potrebbe prendere il posto di quello sopra. Crea un elenco di caratteri accettabili e, per ogni carattere nella richiesta, verifica che esista nella tua lista bianca.

Ora che hai un set di caratteri puliti, controlla l'attraversamento della directory. È più semplice cercare .. in qualsiasi punto della richiesta (i casi pericolosi sono /../ , ../ all'inizio e potenzialmente /.. alla fine e .. solo). Potresti anche voler cercare // , che in senso stretto non è illegale, ma potrebbe essere un problema se interpretato in modo errato (è particolarmente rischioso all'inizio del percorso locale). Verifica che la richiesta inizi con / (non facoltativo in HTTP per RFC),

E poi da lì dovresti essere sicuro accodandolo alla root del documento e controllando accesso (path,R_OK) a verificare che esista ed è leggibile. stat () per verificare che si tratti di un file (non di una directory o di un collegamento simbolico ... a meno che non siano consentiti; ma certamente non una presa di corrente o qualcosa del genere), POI puoi aprirlo per la lettura.

    
risposta data 27.02.2014 - 04:12
fonte

Leggi altre domande sui tag