Domanda:
Esistono lingue "sicure"?
TruthOf42
2014-04-14 18:01:11 UTC
view on stackexchange narkive permalink

Esistono linguaggi di programmazione progettati per resistere all'hacking?

In altre parole, un'applicazione può essere hackerata a causa di un'implementazione non funzionante, anche se il design è perfetto. Sto cercando di ridurre il rischio che uno sviluppatore implementi in modo errato una specifica.

Ad esempio

Domanda

  • Esiste un lingua che affronta molti o la maggior parte di questi problemi? È accettabile definire l'ambito della lingua per un caso d'uso particolare come WebApps, Desktop, Mobile o Server.

Modifica: molte persone hanno affrontato il problema del buffer overflow, o dire che il programmatore è responsabile della sicurezza. Sto solo cercando di farmi un'idea se esistono linguaggi il cui scopo principale fosse quello di prestarsi alla sicurezza il più possibile e ragionevole. Cioè, alcune lingue hanno caratteristiche che le rendono chiaramente più (o meno) sicure della maggior parte delle altre lingue?

Accetteresti qualche variante di Pig Latin come risposta valida?
@BigHomie che mi ha fatto ridere, ma suona un po 'come se non ti piacesse la mia selezione di risposte, in tal caso per favore confuta
Beh, non ho esperienza con Ada, tuttavia non credo che * nessun * linguaggio di programmazione avrebbe impedito il sanguinamento del cuore, e sono pronto a difendere questa affermazione.
@BigHomie Sembri contraddire Tom Leek. Ti va di spiegare perché un linguaggio con il controllo automatico dei limiti degli array e la gestione della memoria non avrebbe impedito Heartbleed?
@Brilliand Ho letto la sua risposta. Per chiarire intendo qualsiasi linguaggio che consente allo sviluppatore di accedere alla memoria dinamica (allocare, copiare, ecc.), Che è ciò che fa quella funzione. In poche parole, la vulnerabilità * non * è un buffer overflow, né a causa del controllo dei limiti di * array *. Questo perché la funzione che invia l'heartbeat utilizza memcpy () e prende un puntatore al presunto "carico utile" inviato dal client, che in realtà può contenere * qualsiasi cosa *, come abbiamo visto.
Inoltre, rileggendo la tua domanda, questo * non * era il risultato dell'overflow del buffer, mentre sono vicini, non so se c'è un termine per questo.
La maggior parte dei linguaggi di programmazione non esiste per prestarsi alla sicurezza, perché lo scopo della maggior parte dei programmi non è essere sicuri. Lo scopo, ad esempio, di un'applicazione bancaria è principalmente quello di monitorare e trasferire i tuoi soldi e secondariamente per essere sicuro. Quindi un linguaggio di programmazione il cui scopo principale è essere sicuri non è molto utile, perché, ad eccezione dell'AV, la sicurezza non è mai lo scopo principale di qualunque cosa tu stia programmando
@KnightOfNi "la sicurezza non è mai lo scopo principale di qualunque cosa tu stia programmando" - questa è un'affermazione piuttosto audace. Direi che lo scopo principale di OpenSSL è la sicurezza, così come qualsiasi protocollo di comunicazione i cui messaggi devono essere segreti
@KnightOfNi ha ragione, è sempre la funzionalità * prima *, per non diramare troppo fuori tema. Un programma viene scritto per risolvere un problema particolare, nel caso di OpenSSL, quel problema è * l'implementazione * di un protocollo di sicurezza. Quanto sia sicura quella * implementazione * è un secondo estremamente vicino alla sua funzione primaria.
indipendentemente dalla lingua, alla fine sei in balia del compilatore http://cm.bell-labs.com/who/ken/trust.html
Un tempo Java si adattava a questa descrizione, ma (iniziando grosso modo con "riflessioni") diventava troppo complesso da verificare come "sicuro" in qualsiasi contesto non banale.
Come può essere che nessuno abbia sollevato linguaggi tipizzati in modo dipendente, che possono applicare staticamente ogni caratteristica di sicurezza che conosco da altre lingue e molto altro
Scaricare la responsabilità della sicurezza da programmatori esperti in sicurezza a compilatori / interpreti / sviluppatori VM / OS che non lo sono sembra una cattiva idea.
Non ci sono linguaggi di programmazione sicuri, ci sono programmatori sicuri. Tutti potrebbero fare cose stupide (non sicure) indipendentemente dalla lingua. Il fatto è che alcune lingue rendono più facile fare cose stupide.
Il problema di progettare un linguaggio pensando alla sicurezza è che potrebbe imporre alcune restrizioni o creare problemi futuri. Considera Java: è stato progettato per essere facile e difficile da interrompere per i loro utenti (controllo dei limiti, eccezioni, ecc.) Ma con l'aumentare della complessità del linguaggio, più difficile è stato renderne l'uso facile, semplice e sicuro. In effetti, oggigiorno le complesse basi di codice in Java non sono né semplici, né facili da mantenere, né sicure.
@BigHomie Forse non capisco appieno heartbleed, ma sembra essere il risultato di un buffer "over-read". Ci sono lingue il cui sistema di runtime genera un errore quando si verifica questo genere di cose, invece di lasciarlo andare. Non l'avrebbe impedito?
@DavidYoung Dovrò porre questa domanda su uno di questi siti per vedere se c'è un nome per esso. Ciò non l'avrebbe impedito, purtroppo, perché l'unico modo per * sapere * quanto doveva essere restituito è affidarsi ai dati dell'utente. Senza controllare la lunghezza presentata con la lunghezza * effettiva *, in questo caso hai * Heartbleed *.
@DavidYoung Ho proposto una modifica a questa domanda, il termine più ben definito per questo fino ad oggi è un [Buffer Over-Read] (http://cwe.mitre.org/data/definitions/126.html).
@Truth - Ho apportato alcune modifiche e aggiunto quelli che pensavo fossero collegamenti interessanti. Si prega di rivedere le proprie intenzioni.
Ho chiarito la domanda con una nuova ultima frase: "Cioè, alcune lingue hanno caratteristiche che le rendono chiaramente più (o meno) sicure della maggior parte delle altre lingue?" E mi chiedo se possa essere riaperto nella sua forma attuale.
Undici risposte:
#1
+56
Tom Leek
2014-04-14 18:23:35 UTC
view on stackexchange narkive permalink

In realtà la maggior parte delle lingue sono "sicure" per quanto riguarda i buffer overflow. Ciò che serve perché un linguaggio sia "sicuro" sotto questo aspetto è la combinazione di: tipi rigorosi, controlli sistematici associati ad array e gestione automatica della memoria (un "garbage collector"). Vedi questa risposta per i dettagli.

Alcuni vecchi linguaggi non sono "sicuri" in questo senso, in particolare C (e C ++), e anche Forth, Fortran ... e, ovviamente assemblaggio. Tecnicamente , è possibile scrivere un'implementazione di C che sia "sicura" e comunque formalmente conforme allo standard C, ma a un prezzo elevato (ad esempio, devi rendere libero () nessuna operazione, quindi la memoria allocata viene allocata "per sempre"). Nessuno lo fa.

I linguaggi "protetti" (per quanto riguarda i buffer overflow) includono Java, C #, OCaml, Python, Perl, Go e persino PHP. Alcuni di questi linguaggi sono più che abbastanza efficienti per implementare SSL / TLS (anche su sistemi embedded - parlo per esperienza). Sebbene sia possibile scrivere codice C sicuro, ci vuole (molta) concentrazione e abilità, e l'esperienza mostra ripetutamente che è difficile e che anche i migliori sviluppatori non possono fingere che applicare sempre i livelli di concentrazione e competenza richiesti. Questa è un'esperienza umiliante. L'affermazione "non usare C, è pericoloso" è impopolare, non perché sarebbe sbagliato, ma, al contrario, perché è vero: costringe gli sviluppatori ad affrontare l'idea che potrebbero non essere i semidei di programmazione che credono di essere, nel profondo della privacy delle loro anime.

Nota, però, che questi linguaggi "sicuri" non prevengono il bug: un overflow del buffer è ancora un comportamento indesiderato. Ma contengono il danno: la memoria oltre il buffer non viene effettivamente letta o scritta; invece, il thread offensivo attiva un'eccezione e viene (solitamente) terminato. Nel caso di heartbleed, questo avrebbe evitato che il bug diventasse una vulnerabilità e avrebbe potuto aiutare a prevenire il panico su larga scala che abbiamo osservato negli ultimi giorni (nessuno sa davvero cosa rende un la vulnerabilità casuale diventa virale come un video di Youtube con un cavallo invisibile coreano; ma, "logicamente", se non fosse stata affatto una vulnerabilità, allora questo avrebbe dovuto evitare tutta questa tragicommedia).


Modifica: poiché è stato ampiamente discusso nei commenti, ho pensato al problema della gestione sicura della memoria per C, e c'è una sorta di soluzione che consente ancora free () per funzionare, ma c'è un trucco.

Si può immaginare un compilatore C che produca "fat pointer". Ad esempio, su una macchina a 32 bit, crea puntatori con valori a 96 bit. Ad ogni blocco allocato verrà concesso un identificatore univoco a 64 bit (ad esempio, un contatore) e viene mantenuta una struttura di memoria interna (tabella hash, albero bilanciato ...) che fa riferimento a tutti i blocchi in base all'ID. Per ogni blocco viene registrata anche la sua lunghezza nella struttura. Un valore del puntatore è quindi la concatenazione dell'ID del blocco e un offset all'interno di quel blocco. Quando viene seguito un puntatore, il blocco viene individuato per ID, l'offset viene confrontato con la lunghezza del blocco e solo allora viene eseguito l'accesso. Questa configurazione risolve double-free e use-after-free. Rileva anche la maggior parte dei sovraccarichi del buffer (ma non tutti: un buffer può essere parte di una struttura più grande, e il malloc () / free () management vede solo i blocchi esterni).

Il "trucco" è il "contatore unico a 64 bit". Questo è vero solo finché non si esauriscono i numeri interi a 64 bit; oltre a ciò, è necessario riutilizzare i vecchi valori. 64 bit dovrebbero evitare questo problema in pratica (ci vorrebbero anni per "concludere"), ma un contatore più piccolo (ad esempio 32 bit) potrebbe rivelarsi un problema.

Inoltre, ovviamente, il il sovraccarico per gli accessi alla memoria può essere non trascurabile (un bel po 'di letture fisiche per ogni accesso, sebbene alcuni casi possano essere ottimizzati), e il raddoppio della dimensione del puntatore implica anche un maggiore utilizzo della memoria per strutture ricche di puntatori. Non sono a conoscenza di alcun compilatore C esistente che applichi tale strategia; al momento è puramente teorico.

Il grosso problema con "non usare C" è l'interoperabilità con altri linguaggi. Praticamente qualsiasi linguaggio supporta la scrittura di un wrapper per le librerie C. Ma non puoi facilmente usare diciamo una libreria Java da C # o Python.
Bene, puoi interfacciarti con (C) Python più o meno in modo affidabile come con C.Penso che il problema sia che saresti vulnerabile a qualsiasi bug nella VM su cui è in esecuzione questo linguaggio sicuro ...
Da quello che ho visto, una cattiva codifica può rendere _qualsiasi_ linguaggio insicuro _ in senso pratico_. Una buona codifica può rendere la maggior parte delle lingue sicure, anche quelle intrinsecamente "non sicure". Rischio anche di essere criticato per aver suggerito che più è alto il livello della lingua, meno è sicuro poiché ti affidi maggiormente a routine scritte da altri, che possono essere soggette a bug più sottili. O per dirla in un altro modo, non ho mai visualizzato una finestra sul mio computer che mi informa che la mia versione di C non è aggiornata e necessita di patch per risolvere i problemi di sicurezza ...
Ma avevi quel tipo di scatola sulla "libreria C standard" ... che è formalmente parte del linguaggio. Allo stesso modo, non hai mai visualizzato un popup sul _Java compiler_ non sicuro, solo il _runtime support code_.
Penso che si potrebbe creare un C sicuro conforme anche liberando memoria, ma richiederebbe un overhead di runtime significativo e probabilmente anche un overhead di memoria significativo. (WRT `free`, la risposta ruoterebbe attorno all'inserimento di controlli del puntatore prima che i puntatori vengano dereferenziati per garantire che l'intervallo sia valido e per segnalare / interrompere se non è valido.
@JohnU Inoltre, non ho mai visualizzato una finestra che mi informa che il codice che ho scritto da solo necessita di patch per risolvere i problemi di sicurezza. In altre parole, quella casella non significa "Wow, un altro esempio della mia piattaforma non sicura!" Significa: "Ehi, una parte del mio codice è stata appena corretta e _ non dovevo nemmeno fare nulla_!"
@MooingDuck: Considera un programma C che alloca un puntatore a un gig di memoria, inserisce alcune cose lì, mostra una rappresentazione esadecimale a 16 cifre sullo schermo, la libera e ripete quel processo 2.000 volte / secondo. Se qualcuno inserisce un numero di 16 cifre che è stato precedentemente visualizzato, il programma mostra i dati che ha inserito in quello spazio. Se qualcuno copia solo l'indirizzo del 517esimo puntatore visualizzato, tutte le allocazioni diverse dal 517esimo potrebbero essere liberate, ma non è possibile che un programma possa saperlo.
@JohnU Non so se sia infuocato per questo, ma penso che la tua affermazione che "scritto da altri lo renda più soggetto a bug sottili" sia completamente sbagliato. Il codice scritto da esperti nel campo a cui appartiene il codice è di ordini di grandezza meno propensi a presentare bug sottili rispetto a cose che hai scritto tu stesso. È per questo che senti cose come "Non scrivere la tua crittografia" "Non reimplementare le strutture dati delle librerie standard".
@MooingDuck: Affinché un linguaggio sia sicuro, deve o vietare le conversioni tra almeno alcuni tipi di puntatori e interi, oppure deve dichiarare apertamente che nulla di importante deve essere memorizzato in aree di memoria accessibili a tali puntatori.
@supercat: Il tuo esempio ha poco senso, poiché si basa su un comportamento indefinito. Ciò significa che il programma _non dovrebbe funzionare_ periodo. Il mio suggerimento è stato che con un po 'di overhead, si potrebbe creare un compilatore C conforme che segnala / interrompe quando si tenta di eseguire un comportamento indefinito come descritto. (Se ho capito male, potresti dover chiarire il tuo esempio)
@MooingDuck: Se ci si sbarazzasse delle istruzioni `free` e si abbandonassero i puntatori senza liberarli, il comportamento del programma sarebbe perfettamente definito, ma non sarebbe mai in grado di recuperare la memoria. Se si include il "free", il comportamento è indefinito, ma un linguaggio sicuro dovrebbe promettere affermativamente di intrappolare un comportamento errante piuttosto che consentire che causi demoni nasali - un po 'difficile da fare se la memoria può essere riciclata mentre esistono riferimenti.
@supercat: sicuramente difficile. Ma non impossibile. In effetti, molte lingue lo fanno.
@MooingDuck: La capacità di convertire una sequenza di numeri in un puntatore legittimo rende la sicurezza essenzialmente impossibile, dal momento che non c'è modo di garantire che un puntatore prodotto selezionando numeri da un cappello non alias un oggetto esistente.
Il C è più giovane di alcuni linguaggi sicuri.
@supercat: Se non alias alcun oggetto esistente, dovrebbe segfault. Se alias un oggetto del tipo corretto, questo è il comportamento desiderato. Tutto quello che devi fare è affermare che il puntatore punta a un oggetto reale del tipo corretto, il che è difficile, ma non impossibile. (assurdamente e impraticabilmente difficile, certo)
@MooingDuck: L'unica cosa che dovrebbe alias un puntatore a un oggetto esistente è un puntatore che è stato in qualche modo derivato da quell'oggetto. Un puntatore derivato da numeri estratti da un cappello non dovrebbe mai indicare nulla. Suppongo che se si usasse un tipo di puntatore abbastanza grande, si potrebbe fare in modo che ogni puntatore codifichi un numero di sequenza, un indirizzo di base, un offset e un checksum crittografico in modo che le sequenze di byte generate arbitrariamente sarebbero riconoscibili come false e nessun puntatore sarebbe mai stato riutilizzato. Se il compilatore richiede che l'indirizzo di base per ogni puntatore contenga un puntatore all'ultimo indirizzo ...
... nello stesso oggetto allocato, i puntatori riciclati sarebbero riconoscibili come tali. Tuttavia, gli oggetti puntatore risultanti sembrerebbero un po 'ingombranti. Avere un tipo di "identificatore di oggetto" potrebbe rendere le cose molto più efficienti.
Prova che è difficile coprire completamente un argomento in una casella dei commenti;) Il mio significato era semplicemente che alcune lingue ti danno più controllo a un livello inferiore rispetto ad altre, puoi usarlo nel bene o nel male. Hai la _selezione_ di importare una libreria conosciuta e attendibile, o di rotolare la tua se lo sai meglio. Quando non hai quel controllo, non hai altra scelta che fare affidamento sul lavoro degli altri e sperare che abbiano eliminato tutti i bug. Alcuni sportelli automatici eseguono Windows XP, un sistema operativo migliore di quello che la maggior parte di noi potrebbe scrivere da soli, ma questo non lo rende un'idea brillante per quell'applicazione.
C è pericoloso, ma ciò non significa che non debba essere utilizzato. C è per super velocità e / o hackery di basso livello. C non è il linguaggio ottimale per la sicurezza e la maggior parte dei programmatori C sarebbe la prima ad ammetterlo. Tutti i bravi programmatori sanno: usa lo strumento giusto per il lavoro giusto.
@supercat @ TomLeek I compilatori C sicuri per la memoria sono * non * solo teorici (sebbene siano certamente più nella classe della prova dei concetti e non consentano l'int al casting del puntatore che hai identificato come problematico, supercat). In questo discorso Andreas Bogk spiega il suo plugin LLVM: [Bug class genocide] (https://www.youtube.com/watch?v=2ybcByjNlq8) [pdf] (http://blog.andreas.org/static/30c3- buffer-overflows.pdf). Introduce solo un overhead di runtime del 100%.
@Perseids: Consente funzioni come qsort che possono operare su tipi di dati arbitrari o richiede che qualsiasi operazione che coinvolge la copia e la manipolazione della struttura debba avere un limite noto in fase di compilazione delle strutture manipolate?
@supercat Non ho visto l'intero discorso di recente, ma per quanto ricordo il casting per void * non dovrebbe rappresentare un problema. Tuttavia, non devi perdere l'informazione che è un puntatore. Quindi lanciare char * -> int-> char * non è possibile (afair).
@Perseids: Se un void * può essere lanciato su un puntatore a doppio indiretto o un puntatore a una struttura contenente un puntatore, ciò consentirà di interpretare come puntatore una sequenza arbitraria di numeri memorizzati in un char [].
@supercat: Non ci ho pensato, giusto. Succede spesso nel normale codice C? Inoltre, pensando al loro design, usano una mappa delle ombre che mappa un puntatore ai suoi limiti. Quindi potrebbe effettivamente andare bene perdere l'informazione che questo era un puntatore, poiché con i falsi indirizzi si finisce con un errore di indice nella ricerca.
Il codice C di @Perseids: normalmente ha bisogno di memorizzare strutture contenenti puntatori in memoria ricevuti tramite `malloc ()` o `calloc ()`. Non esiste alcun meccanismo per garantire che una particolare allocazione non venga lanciata a diversi tipi di puntatori, alcuni dei quali hanno puntatori in luoghi in cui altri hanno numeri.
#2
+56
Pixel Elephant
2014-04-14 21:34:07 UTC
view on stackexchange narkive permalink

Il linguaggio Ada è progettato per prevenire il più possibile errori di programmazione comuni ed è utilizzato in sistemi critici in cui un bug di sistema potrebbe avere conseguenze catastrofiche.

Alcuni esempi in cui Ada va oltre la tipica sicurezza incorporata fornita da altri linguaggi moderni:

  • Il tipo di intervallo intero consente di specificare un intervallo consentito per un numero intero. Qualsiasi valore al di fuori di questo intervallo genererà un'eccezione (nelle lingue che non supportano un tipo di intervallo, dovrebbe essere eseguito un controllo manuale).

  • : = per l'assegnazione = per i controlli di uguaglianza. Ciò evita la trappola comune nelle lingue che usano = per l'assegnazione e == per l'uguaglianza di assegnazione accidentale quando si intendeva un controllo di uguaglianza (in Ada, un'assegnazione accidentale non si compilerebbe ).

  • in e out parametri che specificano se un parametro del metodo può essere letto o scritto

  • evita problemi con i livelli di indentazione del gruppo di istruzioni (ad esempio il recente bug SSL di Apple) a causa dell'uso della parola chiave end

  • i contratti (da Ada 2012 e in precedenza nel sottoinsieme SPARK) consentono ai metodi di specificare le precondizioni e le postcondizioni che devono essere soddisfatte

Lì sono altri esempi di come Ada è stato progettato per la sicurezza fornito nel Libretto sicuro e protetto (PDF).

Naturalmente, molti di questi problemi possono essere mitigati attraverso uno stile di codifica appropriato, revisione del codice, test unitari, ecc. ma averli eseguiti a livello di lingua significa che li ottieni gratuitamente.

vale anche la pena aggiungere che, nonostante il fatto che un linguaggio progettato per la sicurezza come Ada rimuova molte classi di bug, non c'è ancora nulla che ti impedisca di introdurre bug di logica aziendale di cui il linguaggio non sa nulla.

+1 per fare riferimento al libretto sicuro e protetto
Eppure, tutta la sicurezza di Ada non ha impedito il fallimento di Ariane 5.
Questo è un problema, ma non ha due diversi operatori per l'assegnazione / confronto che consente ad Ada di farlo. Dopo tutto, anche "=" e "==" sono diversi. Python distingue `=` (assegnazione) e `==` (confronto) e "non compilerà" (eccezione `SyntaxError`) quando vengono utilizzati in modo improprio. Penso che il punto principale dell'utilizzo di ": =" / "=" invece di "=" / "==" sia prevenire errori di battitura.
@Two-BitAlchemist Non sono del tutto sicuro a quale caso ti riferisci, ma `foo ==" Bar "` non produce un `SyntaxError`.
@ChrisDown Certo che no. Questo è un semplice confronto. `se 'a' = 'b':` d'altra parte ...
@RobertHarvey http: // www.adapower.com / index.php? Command = Class & ClassID = FAQ & CID = 328 - non per segway, ma ADA, né la programmazione era colpa, era un errore di progettazione hardware - cioè il sistema non è mai stato progettato per il Percorso di volo Ariane 5
@TruthOf42: Ariane 5 non è riuscito a causa dell'overflow di un valore numerico a 16 bit. I programmatori hanno ignorato i meccanismi di sicurezza in ADA che avrebbero impedito ciò, perché credevano che il numero non sarebbe * mai * stato superato.
@RobertHarvey Non importa, gli ingegneri hanno utilizzato un sistema hardware (è capitato di utilizzare ADA), progettato per il percorso di volo Ariane 4, dove ha funzionato perfettamente, e poi hanno provato a usarlo per qualcosa per cui NON era progettato (percorso di volo Ariane 5) - anche il fatto che hanno dovuto ignorare qualsiasi cosa dimostra solo di più al punto che erano consapevoli di ciò che stavano facendo e hanno dovuto prendere provvedimenti attivi (a causa della lingua) per fare cose cattive
@TruthOf42 Immagino di stare semplicemente sottolineando che la protezione dai tipi è, in una certa misura, una stampella. A meno che tu non sappia cosa stai facendo, puoi comunque fare in modo che un razzo molto costoso assuma le proprietà aerodinamiche di un tostapane.
@RobertHarvey Non sto sostenendo che se usi solo la lingua giusta tutti i tuoi problemi scompaiono, ma che ha senso usare un particolare tipo di linguaggio per particolari tipi di situazioni, la sicurezza è una di quelle situazioni
Usare = per l'assegnazione e == per il confronto di uguaglianza non è una trappola. La maggior parte dei programmatori è abituata a usare == per i controlli di uguaglianza e = per l'assegnazione. Anche la parola chiave end non è univoca, la maggior parte delle lingue ha un modo per definire l'inizio e la fine di un blocco, la maggior parte usa le parentesi graffe o la fine (ad esempio Lua). Gli altri tre sono completamente validi (anche se in / out è discutibile), ma quei due non sono davvero unici per Ada.
Per l'unico progetto ADA che abbia mai visto, tutto il controllo integrato è stato utilizzato per il debug, il test e l'integrazione, ma quando il prodotto è stato spedito, ** tutti quei controlli integrati sono stati disattivati ​​** perché lo erano " troppo lento "per l'utilizzo da parte dell'utente finale. Parla di testare come corri ...
RobertHarvey dice che hanno disattivato un controllo di sicurezza, è successo del cazzo, e quindi gli obiettivi di sicurezza del linguaggio sono BS. Lol. Penso che il fallimento di Arianne dica di più sull'importanza di una buona analisi dei requisiti e di un corretto utilizzo degli strumenti di qualsiasi altra cosa. Nel frattempo, Ada + SPARK sta ancora promuovendo uno sviluppo ad alta integrità e ha dimostrato di essere un buon investimento per il software a prova di futuro.
#3
+15
DCKing
2014-04-15 21:44:14 UTC
view on stackexchange narkive permalink

La maggior parte dei linguaggi di programmazione di livello superiore al C sono molto più sicuri quando si tratta di errori di programmazione come quelli di Heartbleed. Esempi che si compilano principalmente in codice macchina includono D, Rust e Ada. Non è interessante parlare solo della sicurezza della memoria, secondo me.

Ecco un elenco di funzionalità aggiuntive del linguaggio di programmazione che (penso) rendono molto più difficile scrivere codice non sicuro. Le prime cinque funzionalità espandono le capacità del compilatore nel ragionamento sul tuo codice, quindi tu , un essere umano incline a commettere errori, non devi *. Inoltre, queste caratteristiche dovrebbero anche rendere più facile per un altro essere umano, un auditor, ragionare sul tuo codice. Il codice sorgente di OpenSSL è spesso descritto come un pasticcio e un linguaggio più rigoroso di C avrebbe potuto aiutare a rendere più facile ragionare. Le ultime due funzionalità riguardano problemi di contesto che influenzano anche la sicurezza.

  • Un sistema di tipi rigorosi: rende più facile ragionare sulla correttezza del programma. Elimina alcuni attacchi di input.
  • Immutabile per impostazione predefinita: avere valori immutabili come contenitore di dati primario significa che è molto più facile ragionare sullo stato del tuo programma.
  • Disabilitato o limitato non sicuro: Non permettere cose spaventose come l'aritmetica del puntatore (es. Go), o almeno permetterlo solo se racchiuso in grossi avvisi (Rust). Tieni presente che un linguaggio privo completamente di aritmetica dei puntatori è escluso per l'uso in un numero enorme di applicazioni che richiedono un accesso di basso livello.
  • Tempo di compilazione controllo della contaminazione : espande il sistema di tipi per consentire l'identificazione di valori contaminati : valori che dipendono in qualche modo in base all'input. Il compilatore potrebbe quindi (condizionatamente) vietare operazioni con un valore contaminato che fanno trapelare informazioni a osservatori esterni, come la ramificazione su tale valore. Ciò potrebbe impedire o almeno migitare determinate classi di attacchi temporali. Per quanto ne so, questi sono disponibili solo negli strumenti di analisi del codice statico e non nei compilatori stessi?
  • Tipi dipendenti: i tipi dipendenti sono un mezzo per dire al compilatore che "quièun Int i cui valori sono compresi tra 2 e 87" o "quièuna Stringa di lunghezza massima 12 contenente solo caratteri alfanumerici ". Il mancato rispetto di questi requisiti provoca un errore di compilazione e non un errore di runtime con risultati probabilmente non sicuri. Questa funzione è disponibile in Idris e in alcuni linguaggi di dimostrazione di teoremi.
  • Assenza di garbage collection: la garbage collection è un grosso problema per la sicurezza del linguaggio - crea pause di garbage collection nel tuo programma. Questi interrompono le informazioni sulle perdite sullo stato del programma e consentono che si verifichino attacchi temporali. Quando il garbage collector viene invocato è impossibile (o nella migliore delle ipotesi incredibilmente difficile) da prevedere come sviluppatore, tuttavia, e soggetto a enormi cambiamenti anche per la più piccola quantità di modifiche al codice.
  • Prestazioni, portabilità Interopabilità di &: Potrebbe andare bene se hai bisogno di un programma sicuro e lento che gira solo sulla piattaforma PowerPC, ma non aspettarti che qualcun altro lo utilizzi per una libreria TLS multipiattaforma. OpenSSL è popolare proprio perché è veloce e funziona ovunque, da oscuri router basati su MIPS a server SPARC in parallelo massicciamente e tutto il resto. Inoltre qualsiasi programma o runtime nel mondo può interfacciarsi con OpenSSL come libreria perché utilizza convenzioni di chiamata C.

Dalla mia conoscenza limitata delle lingue, nessun linguaggio lo fa tutti questi. Rust è un esempio di un linguaggio che copre molti: è rigoroso, immutabile per impostazione predefinita, ha limitazioni di sicurezza, non richiede la raccolta dei rifiuti ed è abbastanza performante e portatile. Il controllo della contaminazione in fase di compilazione e i tipi dipendenti attualmente sembrano essere funzionalità esotiche che richiedono strumenti aggiuntivi di analisi statica del codice o nuovi linguaggi, purtroppo.

* Vedi anche: verifica formale

Questa è una buona lista. I tipi dipendenti sono un grosso problema. Haskell, con le estensioni appropriate (come le famiglie di tipi) copre la maggior parte dell'elenco. Tuttavia, il modello di esecuzione non è così eccezionale per prevenire gli attacchi a tempo. La valutazione pigra è una "immagine speculare" della raccolta dei rifiuti.
Non tutti i garbage collector sono tipi stop-the-world. Esistono garbage collector incrementali e simultanei.
Il mio commento sulla raccolta dei rifiuti è l'esempio più rilevante della creazione di imprevedibilità nei tuoi programmi. Non è saggio creare un comportamento nel tuo programma che sia indeterminato o imprevedibile per te come sviluppatore. Le caratteristiche di questo comportamento potrebbero fornire a un utente malintenzionato troppe informazioni. Puoi rendere il tuo programma imprevedibile con garbage collection (qualunque tipo di GC utilizzi), valutazione pigra, concorrenza e altre cose. Nella maggior parte delle lingue raccolte dai rifiuti è in effetti inevitabile, il che è un male.
Il commento del garbage collector è ingiusto perché i linguaggi GC prevengono enormi quantità di vulnerabilità comuni mentre gli attacchi side-channel dovuti a GC sono quasi inesistenti nel mondo reale. Ho scoraggiato i GC per i sistemi di crittografia o anonimato a causa di questa debolezza, ma risolvono molti problemi nelle app normali. Per non parlare del fatto che ottieni molte soluzioni ai problemi di cui parli digitando la raccolta dei rifiuti in tempo reale in Google, inclusi i prodotti Java commerciali.
@NickP No. * La sicurezza della memoria * è ciò che impedisce enormi quantità di vulnerabilità comuni, non la garbage collection. Accade solo che molti linguaggi di Garbage Collection siano sicuri per la memoria: ma la Garbage Collection non implica necessariamente la sicurezza della memoria né la sicurezza della memoria implica necessariamente la Garbage Collection. Inoltre "la raccolta dei rifiuti in tempo reale" non è una soluzione a nessun problema che ho citato, poiché anche i raccoglitori di rifiuti in tempo reale creano complesse imprevedibilità all'interno dei vostri programmi. Sono d'accordo che le lingue garbage raccolte sicure per la memoria siano preferibili rispetto alle lingue non sicure e non GC.
Buon punto sulla sicurezza della memoria. Errato nella raccolta dei rifiuti in tempo reale. Il punto è che, indipendentemente dalla loro tecnica, lasciano l'esecuzione prevedibile. La maggior parte non è ottimizzata per la mitigazione del canale nascosto perché alla maggior parte degli sviluppatori non interessa. Puoi modificarli per mitigare i canali di temporizzazione. Mi sono solo assicurato che le operazioni guidate da segreti si concludessero sempre in un determinato periodo di tempo. Se volessi di più, potrei usare un'esecuzione asincrona sensibile alla priorità. Le misurazioni mostrano che funziona e un accademico ha anche recentemente costruito un processore per fare la stessa cosa. La soluzione è presente ma non applicata. Comune.
Come ho detto, però, sconsiglio l'uso anche di GC in tempo reale se i canali nascosti * davvero * contano. Di solito sono importanti solo all'interfaccia, la cui sincronizzazione può essere controllata anche con RT GC. Può e ha.
@NickP Va bene, direi che la garbage collection è in qualche modo un pignolo. Anche se puoi controllare la sua imprevedibilità, * è * un canale nascosto aggiuntivo di cui preoccuparti, anche se minore. Come ulteriore punto, la garbage collection è piuttosto un ostacolo per le prestazioni e il punto di portabilità: nessuno scriverà una libreria di sicurezza in un linguaggio GC con prestazioni per lo più lente e un grande runtime associato che deve essere caricato per il suo utilizzo. Quindi le lingue con GC sono di uso più limitato.
Sono d'accordo che sia ancora un canale nascosto: ho detto tanto su Freenet che utilizza Java. Gli sviluppatori di software di sistema lo eviteranno spesso per i motivi che hai menzionato. Tutto vero. La buona notizia è che le eccezioni stanno lentamente aumentando.
#4
+6
moe
2014-04-15 00:59:22 UTC
view on stackexchange narkive permalink

Nello spirito generale di ciò che stai chiedendo, penso che il linguaggio E (la "piattaforma distribuita sicura per oggetti puri e linguaggio di scripting p2p") sia piuttosto interessante, in quanto sta tentando per offrire in modo sicuro funzionalità / modelli di calcolo non generalmente disponibili.

potresti fornire qualche dettaglio in più per la risposta ada? Grazie!
E è un sicuro per progettazione contro molte cose che il linguaggio unite al modello di sicurezza delle capacità degli oggetti. Questo modello può essere utilizzato per implementare molti tipi di politiche e sistemi di sicurezza in un modo che supporti POLA. Ha anche il supporto per il calcolo distribuito. I suoi sostenitori, specialmente Combex, hanno creato chat sicure in (100 linee?), Creato un browser sicuro e reso un prototipo di desktop sicuro immune a molti problemi che Windows ha. Quindi, direi che si qualifica più che come un linguaggio sicuro. L'implementazione è dove saranno i draghi, specialmente facendo affidamento su Java.
#5
+5
KnightOfNi
2014-04-14 18:19:17 UTC
view on stackexchange narkive permalink

All current (meaning still updated) programming languages are designed to have as few inherent security flaws as possible, but at the end of the day it's (almost always) the programmer who is responsible for security flaws, not the language he's using.

EDIT: As @DCKing pointed out, not all languages are equal, and I'm not saying it's a good idea to pick one at random and try and make it work. I am saying that a (very) talented C programmer can make a program just as secure as a semantically identical program written in a higher level language. My point is that we should recognize that some languages make it easier to make mistakes, but also know that in the end it's the programmer's mistake, not the language's (with few exceptions)

Tuttavia, non è questo ciò che viene chiesto.
@user1737909 Risponde alla domanda. Tutti i linguaggi di programmazione sono progettati per non essere intrinsecamente insicuri, ma non importa perché è il modo in cui il programmatore li usa che determina quanto sia sicuro un sistema.
@KnightOfNi Sento che questa risposta equivale a dire che un castello è sicuro solo quanto i soldati che lo difendono, il che è in parte vero, ma c'è un motivo per cui i castelli sono stati progettati come erano, quindi è più facile da difendere e meno lavoro ha essere fatto dai soldati
@TruthOf42 Bene, "un linguaggio di programmazione in cui il programmatore lavora meno" è solo un linguaggio di programmazione di livello superiore. Continuando con la tua analogia con il castello, la domanda è "quale tipo di roccia è il migliore per le mura del mio castello?" La risposta è che a meno che tu non stia usando il gesso, nessuno andrà per le tue pareti (nella maggior parte dei casi), sfrutteranno il fatto che tu (l'architetto) hai dimenticato di aggiungere una porta al castello o te ne sei andato in un passaggio segreto. Sono quasi sempre i sistemi ad essere sfruttati, non i linguaggi stessi (con, ovviamente, alcune eccezioni).
Nella mia analogia, il castello è la lingua ei soldati sono il programma. Se si progetta un castello con un ingresso molto ampio è più difficile da difendere, ma se si progetta un castello in cui solo una persona alla volta può entrare, è più facile implementare una politica che garantisca che la persona che entra sia autorizzata. Quello che sto cercando di ottenere è che puoi progettare un linguaggio in modo tale da INCORAGGIARE buone pratiche di sicurezza.
@TruthOf42 Lo so. L'ho cambiato per mostrare il mio punto. Penso che il castello rappresenti meglio l'intero sistema / programma perché ha così tante cose diverse che si sommano alla sua sicurezza contro i nemici, ognuna delle quali è una funzione. Le cose che rendono sicure le funzioni (l'abilità e l'integrità delle guardie, avere una porta, ecc.) Sono gli elementi del linguaggio. Farlo al contrario, come hai fatto tu, mi sembra controintuitivo, quindi l'ho cambiato per chiarire meglio il mio punto.
È più facile scrivere programmi insicuri in C e PHP che scriverli in Ada o Scala. Non fingere che la scelta del linguaggio sia arbitraria e che tutto spetti al programmatore. Alcuni strumenti sono migliori di altri.
@DCKing Non l'ho mai detto, e non è questo il problema. La domanda è "[esistono] lingue il cui scopo principale [è] prestarsi [se stessi] alla sicurezza il più possibile e ragionevole?" La mia risposta è stata che pochi linguaggi, se non nessuno, sono progettati per essere INsecure, ma che la sicurezza di un codice è responsabilità del programmatore. Chi ha detto qualcosa sul fatto che tutte le lingue siano uguali?
@KnightOfNi Per chiarire, non l'ho letto necessariamente nella tua risposta. La tua risposta può essere interpretata in modo da implicare ciò, tuttavia, e ho ritenuto che fosse necessario un commento per assicurarmi che le persone non lo leggessero in quel modo. È inteso come un'aggiunta, non come una correzione :)
@DCKing Oh, OK. Grazie per avermi fatto sapere. Lo farò riferimento nella mia risposta.
Continuerò comunque a non essere d'accordo (e d'accordo con @TruthOf42). Diciamo, ad esempio, che un linguaggio che fornisce un buon modo per evitare di trattare con le stringhe, forse una libreria letterale XML che esegue l'escape automatico correttamente a seconda di dove ti trovi nell'AST XML (nodo, ecc.), Sarebbe di grande aiuto.
@user1737909 Sarebbe davvero di grande aiuto. Qual è il tuo punto?
Il punto è: questa è la domanda. Lingue con tali caratteristiche.
@user1737909 Non pretendo di capire quello che hai appena detto, ma la domanda NON ERA "alcune lingue sono migliori / più facili di altre per la sicurezza?" ERA "ci sono linguaggi specificamente progettati per essere il più sicuri ragionevolmente possibile?"
#6
+5
Steffen Ullrich
2014-04-15 00:12:34 UTC
view on stackexchange narkive permalink

Non esiste una lingua sicura. Se una lingua fornisce una sicurezza sufficiente per il tuo problema, dipende molto dal problema che stai cercando di risolvere. Ad esempio, se stai scrivendo un'applicazione web, la sicurezza della maggior parte dei linguaggi utilizzati in questo contesto (ad esempio Java, PHP, JavaScript ... aggiungi il tuo preferito) è sufficiente per evitare cose come il buffer overflow, ma anche i linguaggi più fortemente tipizzati non lo fanno offrire supporto intrinseco per cose specifiche del web, ad es come rendere impossibile o almeno difficile introdurre bug di Cross-Site-Scripting o simili. E nessuna lingua ti proteggerà da un modello di fiducia errato, come la fiducia nei server DNS (riassociazione DNS ecc.), L'attuale modello PKI o includendo script di terze parti (ad es. Fuori dal tuo controllo) nella tua applicazione web (in genere annunci o Google Analytics) .

Quindi la scelta di una lingua corretta potrebbe aiutarti un po ', ma non esiste una spada magica di sicurezza.

+1 per "spada magica di sicurezza". E anche tutte le altre cose :)
Non esiste un linguaggio "sicuro" perché "sicuro" non è probabilmente mai definito (e non definibile). Ci * sono * lingue che sono * più * sicure di altre lingue, tuttavia, dato un modello di minaccia, e penso che sia di questo che si tratta.
A proposito, cosa intendi con "linguaggi più fortemente tipizzati non offrono supporto intrinseco per cose specifiche del web"? Non riesco a vedere quanto i linguaggi tipizzati fortemente non offrano quel supporto o addirittura come siano peggiori nel supportare quelle funzionalità!
Non sono peggiori, ma non forniscono supporto intrinseco per vari tipi di stringhe (html, javascript, css, url ...) e si assicurano che tutto venga sfuggito nel modo giusto se concateni diversi tipi di stringhe (il le regole sono complicate e talvolta dipendono dal browser). Quindi in questo momento la maggior parte degli sviluppatori lo fa a mano o con l'aiuto di un toolkit, ma spesso lo fa male e causa attacchi XSS. Probabilmente puoi implementarlo, ma al momento non c'è almeno alcun supporto intrinseco nei principali linguaggi utilizzati per lo sviluppo web.
@SteffenUllrich: cerca Yesod. Oh, aspetta, stai spostando i pali della porta. "I principali linguaggi utilizzati per lo sviluppo web" non hanno sistemi di tipo forte.
Yesod è un framework che devi usare e non un linguaggio. E mentre haskell potrebbe essere un bel linguaggio, dubito che qualcuno lo stia usando per uno sviluppo web serio. Sì, so che ci sono ricerche interessanti per una migliore sicurezza intrinseca, ma non aiuta se non si abituano a cose serie :(. Sfortunatamente devi lavorare con ciò che gli sviluppatori possono attualmente e non aspettarti che saltino su la prossima cosa brillante, almeno se la curva di apprendimento è troppo alta.
Molte persone scrivono applicazioni web "serie" in Haskell, Scala e F #, tutte tipizzate in modo molto forte ed esoteriche alla ricerca del programmatore PHP occasionale. Ti farò sapere che sono più produttivo e capace nello scrivere app web in Scala che in PHP, quindi a ciascuno la sua. La cosa divertente del tuo commento è che la necessità di un framework per fare le cose rende le lingue in qualche modo meno capace per un serio sviluppo web. Ti sfiderò a trovare qualsiasi applicazione web seria e sicura che * non * utilizzi un framework (anche se fatto da te).
Forse hai ragione e haskell, scala ecc. Forniscono framework sicuri e sono usati abbastanza per essere visibili. Non dubito che tu possa essere molto produttivo con loro. Ma vedo che nemmeno aziende come ebay, google, facebook ecc. Che hanno programmatori decisamente intelligenti sono immuni ai tipici problemi del web 2.0 come XSS o CSRF. Quindi immagino che i framework che usano non siano ancora abbastanza sicuri. E la domanda originale posta sulle lingue protette, non sui framework sicuri.
Non è affatto corretto: Opa e Ur / Web sono linguaggi progettati per essere immuni a molti problemi tipici del Web 2.0. Quindi esistono. Le persone semplicemente non li usano molto.
#7
+4
Mike Scott
2014-04-15 11:13:11 UTC
view on stackexchange narkive permalink

Ricorda che per la maggior parte dei linguaggi di programmazione, devi preoccuparti della sicurezza di due linguaggi. C'è la lingua che stai effettivamente utilizzando, e poi c'è la lingua in cui sono scritti il ​​compilatore o l'interprete, che spesso è diversa. (Tecnicamente, ce n'è un terzo, che è il microcodice della CPU stessa.) Un problema di sicurezza in uno di questi linguaggi può rendere insicuro il tuo programma.

Pensavo che tutti i ragazzi fighi dovessero scrivere il compilatore della loro lingua in quella lingua.
La maggior parte dei linguaggi compilati con codice nativo sono self-hosting (implementati in se stessi). Tuttavia, le lingue più diffuse in questi giorni sono ospitate in una VM, il che lo rende impossibile. Puoi ancora scrivere un compilatore da source a bytecode in quella lingua (ad esempio javac) ma la VM che interpreta il bytecode deve essere codice nativo.
Tuttavia, preferirei di gran lunga preoccuparmi dei buffer overflow solo nel codice della JVM stessa invece di preoccuparmene in ogni singola libreria da cui dipende il mio codice.
Buon punto. Da qui l'importanza di tutta la ricerca "certificante" di compilazione, interpretazione e gestione della memoria. Penso che la co-simulazione sia una delle soluzioni migliori. Crei un modello di come la lingua di partenza e quella di destinazione agiscono. Esegui i dati in ognuno di essi per ottenere tracce di esecuzione. Confronta le proprietà delle tracce per assicurarti che corrispondano in funzione e rischio. Un progetto sta utilizzando con successo questa tecnica per un compilatore di ottimizzazione da C a MIPS.
#8
+3
Superbest
2014-04-15 10:09:15 UTC
view on stackexchange narkive permalink

In primo luogo, in realtà non scrivi programmi in linguaggi di programmazione. Scrivi istruzioni per il compilatore che descrive che tipo di programma vuoi, e il compilatore produce un programma, nel suo modo peculiare, che si spera (se il tuo compilatore è ben progettato) fare la stessa cosa descritta dal codice sorgente. Tutti i programmi, quando sono in esecuzione, sono in "linguaggio macchina" - sono una serie di numeri che vengono interpretati in un certo modo quando vengono caricati nella RAM e inseriti nella CPU. Il linguaggio macchina non è progettato pensando alla robustezza dell'hacking, quindi nessun linguaggio compilato può essere veramente "resistente" all'hacking, perché il programma effettivo sarà comunque in linguaggio macchina. Qualsiasi linguaggio interpretato o VM continuerà a funzionare in un framework nativo che è compilato alla fine in linguaggio macchina, quindi il problema persiste.

In secondo luogo, la maggior parte dei linguaggi reali sono completi di Turing. Ciò significa che qualsiasi compito che può essere svolto da uno di loro può essere portato a termine da tutti. Pertanto, non è possibile rendere impossibile "l'hacking" (se l'hacking significa scrivere programmi dannosi); romperebbe la completezza di Turing.

A questo punto vale la pena chiarire cosa intendi per hacking. Dato che hai menzionato Heartbleed, immagino che tu non lo intenda nel senso di Stallman ("armeggiare scherzoso").

Se intendi persone che scrivono programmi che accedono direttamente alla memoria e rubano dati o modificano altri programmi (come virus o keylogger), questo non è un problema che una lingua può davvero affrontare. Un compilatore può aiutare, avendo una funzione aggiuntiva per produrre codice macchina offuscato durante la compilazione, ma alla fine è ancora possibile per un abile hacker della memoria trovare la sua strada. La soluzione a questo problema è la progettazione del sistema operativo: un sistema operativo dovrebbe eseguire il sandbox dei programmi e non consentire a un programma di alterare la memoria che appartiene a un altro programma. Questo fa parte di ciò che fa l'UAC in Windows (sebbene Sandboxie sia un esempio migliore).

C'è un avvertimento qui: alcuni linguaggi, come C # o Java hanno funzionalità (più correttamente, il compilatore e la VM in cui vengono eseguiti i programmi hanno funzionalità) che controllano se qualche programma sta cercando di masticare nella memoria di un altro programma, e quando ciò accade genera errori come IllegalAccessException (ad esempio, keylogger.exe non dovrebbe essere in grado di leggere il valore Credit_card_number da internet banking application.exe ). Ovviamente, questo richiede di tenere traccia di quale memoria appartiene a quale programma, che ha alcune prestazioni e costi di impegno non banali. Alcuni linguaggi "più semplici" come il C non ce l'hanno - questo è il motivo per cui molti hack come i virus sono scritti in C. Oggigiorno devi essere intelligente nell'eludere l'UAC, ma ai tempi di Windows 98 le persone potevano fare di tutto di cose folli sul tuo computer / sistema operativo leggendo e scrivendo in memoria che non avrebbero dovuto. Nota che anche in C # hai ancora la possibilità di usare normali puntatori simili a C (che i linguaggi chiamano unsafe e richiedono di contrassegnare come tali nel codice) se lo desideri, sebbene CLR probabilmente lo farà contenere il tuo hack al suo interno, a meno che non trovi un buco di sicurezza nel CLR che ti permetta di entrare nel resto della memoria.

Il secondo tipo di hacking è sfruttare un bug in un programma esistente. Questa è la categoria a cui appartiene il cuore sanguinante. Con questo, la domanda è se il programmatore commette un errore o meno. Ovviamente se la tua lingua è qualcosa come Brainfuck o Perl che è molto difficile da leggere, è probabile che commetterai degli errori. Seèun linguaggio con molti "gotcha" come C ++ (vedi "classico" if (i = 1) vs. if (i == 1) o il C contest di offuscamento) quindi potrebbe essere difficile cogliere gli errori. In questo senso, progettare per la sicurezza è in realtà solo un banale caso speciale di progettazione per ridurre al minimo gli errori del programmatore.

Nota che il bug Heartbleed, sia che si tratti di un sabotaggio intenzionale o di un errore onesto, era un problema con l ' algoritmo utilizzato (l'autore "si è" dimenticato "di controllare la dimensione), quindi nessun compilatore a meno di un'IA come intelligente come un essere umano molto intelligente potrebbe sperare di rilevarlo; sebbene la violazione di accesso risultante, plausibilmente, avrebbe potuto essere colta con una gestione intelligente della memoria.

In conclusione

Ci sono due tipi di preoccupazioni riguardo all'hacking:

  1. Un programma è stato programmato in modo errato e ti consente di fare cose che non dovresti. Per esempio. Il server Gmail consente a tutti di vedere le tue e-mail, invece di richiedere loro di inserire prima il nome utente e la password corretti, perché qualcuno ha commesso un errore durante lo sviluppo del software del server. Include bug, vulnerabilità, ecc.

  2. Un programma viene manipolato dal programma dannoso dell'hacker. Include virus, keylogger e altri malware.

(1) può essere risolto rendendo un linguaggio più rigoroso ed esplicito, in modo che il rilevamento degli errori sia più facile, ma alla fine solo molto semplice gli errori possono essere rilevati da strumenti automatizzati e, per quanto riguarda i "tripwires" come il controllo della portata di Ada, si può sostenere che riconoscere la possibilità di un errore è necessario per pensare di aggiungere il controllo in primo luogo, e riconoscere la possibilità è già la parte più difficile.

(2) non può essere risolto cambiando la lingua. Se crei un linguaggio in cui è molto difficile scrivere applicazioni malvagie, gli hacker useranno semplicemente un altro linguaggio e non avranno difficoltà aggiuntive a manipolare i programmi scritti nella tua lingua perché alla fine vengono comunque eseguiti come codice macchina. Può essere risolto creando un sistema operativo che controlli molto attentamente i programmi in esecuzione al suo interno, ma poi diventa una questione di (1) problemi di tipo nel codice sorgente del sistema operativo .

La completezza di Turing significa fondamentalmente che una lingua può * emulare * qualsiasi altro computer (=> programma => compilatore => caratteristica del linguaggio) esistente. Ciò non significa che una funzione che stai emulando possa mai essere così potente o sfruttabile, come se fosse nativa della lingua. Java è un ottimo esempio: potresti * emulare * puntatori usando un array e int "puntatori", ma dovresti comunque scrivere intenzionalmente codice per consentire a un buffer di overflow. E non influenzerebbe nulla al di fuori dell'array.
La prima frase è sbagliata. Scrivi programmi in un linguaggio di programmazione. Il compilatore traduce quel programma nello stesso programma in una lingua diversa.
Questa risposta fraintende completamente l'argomento dei linguaggi di programmazione. Esistono linguaggi di programmazione di livello superiore per migliorare * il codice macchina generato *
Tante cattive affermazioni. Esistono linguaggi di programmazione di livello superiore per rendere * migliore * il codice macchina generato in qualche modo, ad es. più sicuro. Se il tuo linguaggio di livello superiore garantisce che l'assembly generato non consente determinate condizioni non sicure in fase di esecuzione, il tuo linguaggio è più sicuro. In secondo luogo, come sottolineato, fraintendi completamente ciò che implica la completezza di Turing. Solo perché la completezza di Turing è lì per uno sviluppatore, non significa che sia lì per un aggressore. Infine, l'accesso locale ai file binari non fa parte del modello di minaccia per la maggior parte degli attacchi, inclusi quelli su OpenSSL.
Ammetto che alcune delle mie affermazioni non sono esattamente vere: le ho scritte deliberatamente in questo modo per mantenere le cose semplici e concise. Penso che sia facile vedere quali, ed è facile scoprire la versione corretta. Tuttavia, penso che i punti che ho esposto siano validi: ad esempio, la completezza di Turing è una preoccupazione rilevante se si desidera vietare determinati tipi di programmi in base alla progettazione. Dipende un po 'da come definisci l'hacking: se l'hacking sta creando deliberatamente (manomettendo) o accidentalmente (bug) un programma pericoloso, la completezza di Turing non garantisce la possibilità di un tale programma?
Considera anche il "possibile spazio del programma" del tuo codice sorgente rispetto all'assembly compilato. Anche se hai progettato la tua lingua per limitare questo spazio, la restrizione non esiste per l'assemblaggio, quindi il malware che manomette la memoria può comunque far eseguire al tuo programma cose che la tua lingua è stata progettata per non consentire.
@DCKing Le modifiche sono benvenute se pensi di poter migliorare la risposta, ma rispetterò la prima frase. Penso che fondamentalmente, i linguaggi di alto livello siano venuti per salvare il programmatore dal fastidio di ricordare le istruzioni della macchina e di scherzare con un milione di gotos. Il fatto che il compilatore sia ora in grado di eseguire ottimizzazioni automatiche e migliorare la sicurezza è solo un vantaggio secondario. Il linguaggio non esiste per migliorare il codice generato: a volte, le persone possono persino accettare un codice più lento (peggio?) Perché il linguaggio di alto livello semplifica il loro lavoro.
Infine, considera la sicurezza del programma (implementazione) rispetto alla sicurezza dell'algoritmo. Dovrebbe essere chiaro il motivo per cui i problemi con quest'ultimo non possono essere risolti con la progettazione del linguaggio.
La completezza di Turing non ha nulla a che fare con questo. Puoi concepire malware per regex o altri sistemi non completi di Turing. Le macchine di Turing sono una classe di equivalenza sulla computabilità teorica degli algoritmi. Non è una classe di equivalenza di sicurezza, che è un concetto pratico del mondo reale. In effetti, spiegami come faresti per "sfruttare" la classica macchina di Turing a nastro singolo? È Turing completo, quindi deve essere sfruttato, giusto? Se usi un termine come macchina di Turing in un contesto come questo, inizia a suonare come olio di serpente per me.
lasciaci [continuare questa discussione in chat] (http://chat.stackexchange.com/rooms/13912/discussion-between-superbest-and-dcking)
#9
+3
Manishearth
2014-04-17 06:34:56 UTC
view on stackexchange narkive permalink

Esistono molte lingue sicure. Direi che un linguaggio con gestione della memoria e thread safety è sicuro quanto un linguaggio può ottenere.

Tuttavia, la maggior parte di questi sono inefficienti. La raccolta dei rifiuti è costosa e le lingue interpretate lo sono di più. Ed è per questo che fino ad oggi le applicazioni di grandi dimensioni sono scritte in un C / C ++ non sicuro per la memoria.

Recentemente ho giocato con Rust e a me sembra essere un linguaggio "sicuro" nel senso che è stato in parte progettato per questo.

È un linguaggio compilato come C ++ e offre anche puntatori e concorrenza. (Garbage Collector non necessario)

Tuttavia, non trascura la sicurezza della memoria dei puntatori e la concorrenza con esso. Rust è un linguaggio che non si fida del programmatore e in fase di compilazione controlla l'utilizzo sospetto dei puntatori. Esistono più tipi di puntatori / riferimenti (presi in prestito, posseduti, ecc.) E alcuni di essi hanno regole rigide su di essi. Ad esempio, non si può:

  • prendere un riferimento a un puntatore di proprietà e quindi mutare il puntatore di proprietà
  • passare un riferimento al di fuori della durata di un oggetto (i riferimenti non sono t solo numeri che possono essere battuti come in C ++)
  • spostarsi su un puntatore di proprietà e accedere alla variabile originale

Esistono regole simili che garantiscono la sicurezza del thread. Se si desidera, possono bypassare molti di questi controlli utilizzando caselle contrassegnate come non sicure ("fidati di me, so cosa sto facendo"), o rallentare i puntatori raccolti dai rifiuti. Esistono anche modi più rustici (ed efficienti) per farlo utilizzando una combinazione di cloni e riferimenti, che variano a seconda dell'utilizzo.

#10
+1
AJ Henderson
2014-04-14 18:28:08 UTC
view on stackexchange narkive permalink

I linguaggi Managed Type Safe fanno molto per prevenire questo genere di cose fornendo la convalida dei tipi automaticamente e spostando ulteriormente l'esecuzione del codice dalla CPU stessa, tuttavia ciò non esclude la possibilità di bug nell'implementazione del sistema utilizza la lingua per eseguire il mapping alla CPU (ad esempio, CLR in .Net o JVM in Java). Inoltre non esclude la possibilità di bug in un'applicazione che potrebbe renderla vulnerabile alla manipolazione o alla perdita di dati per se stessa.

Migliorano notevolmente la sicurezza del sistema, ma anche sono più voluminosi, più lenti e più limitati nelle funzioni a causa del sovraccarico del motore di esecuzione che devono eseguire per fornire quella funzionalità.

L'indipendenza dai tipi non ha nulla a che fare con macchine virtuali come JVM o CLR, potresti facilmente avere un linguaggio compilato con l'indipendenza dai tipi. E avere un compilatore che impone i controlli dei limiti degli array è ... facile. Aggiunge un po 'di overhead, ma poi di nuovo perché vuoi scrivere assembler? E certo, anche i compilatori sono programmi e possono avere bug, ma almeno ci sono linguaggi con definizioni formali rigorose che un compilatore può provare (o meno).
@kutschkem - sì, ma i linguaggi sicuri di tipo MANAGED sì. Il codice gestito richiede qualcosa per la gestione. A proposito, non parlo affatto contro le lingue gestite. Sono uno sviluppatore C #.
@kutschkem: Un requisito fondamentale per la sicurezza è essere in grado di garantire che la memoria di nessun oggetto sia soggetta a recupero mentre potrebbe esistere qualsiasi riferimento ad essa. Richiedere che tutti i riferimenti siano archiviati in ogni momento in un modo che il gestore della memoria conosce può facilitare notevolmente questo, e l'utilizzo di una VM gestita lo facilita notevolmente.
Un linguaggio compilato può gestire facilmente i riferimenti ed eseguire operazioni di garbage collection senza richiedere una VM. Inoltre, un tale linguaggio può essere implementato da solo, quindi non devi preoccuparti dei bug relativi al C nel codice C utilizzato per implementare la tua VM.
#11
  0
user25221
2014-04-16 19:47:33 UTC
view on stackexchange narkive permalink

Ci sono certamente lingue progettate per essere sicure, ma nessuna perfetta. Ada, ad esempio, ti consente di specificare un intervallo consentito per le variabili intere e genera un'eccezione se escono da tale intervallo. Suona bene, ti ho risparmiato il controllo manuale. Il problema è che se non devi controllare manualmente è facile impostare questo meccanismo e poi dimenticare di considerare le conseguenze, cioè eccezioni di numero intero fuori intervallo. Hai appena creato un vettore per gli attacchi Denial of Service.

La sicurezza è un processo. Il linguaggio può aiutare, ma nella migliore delle ipotesi può solo ridurre la possibilità di errori e nel processo di solito ne crea di nuovi e spesso anche più sottili. Probabilmente il C, per natura più facile da comprendere appieno e completamente deterministico (nessuna raccolta di dati inutili in background, ad esempio), è ideale per scrivere codice sicuro. E abbastanza sicuro, quando guardi un sacco di codice critico per la sicurezza è scritto in C. Per essere onesti con Ada, si tratta più di affidabilità che di sicurezza.

C è tutt'altro che ideale: è troppo complesso, ha un comportamento indefinito e presenta problemi di sicurezza nei costrutti più comuni (ad esempio array, stringhe). Pascal e Modula-2 sono molto migliori per l'analisi grazie alla semplicità, leggibilità, coerenza e facile compilazione.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...