Domanda

Quando scrivi il codice, programmi consapevolmente in modo difensivo per garantire un'elevata qualità del programma ed evitare la possibilità che il tuo codice venga sfruttato in modo dannoso, ad es.attraverso exploit di buffer overflow o iniezione di codice?

Qual è il livello "minimo" di qualità che applicherai sempre al tuo codice?

È stato utile?

Soluzione

Nel mio lavoro, il nostro codice deve essere della massima qualità.
Quindi ci concentriamo su due cose principali:

  1. Test
  2. Revisioni del codice

Quelli portano a casa i soldi.

Altri suggerimenti

Similmente ad abyx, nel team di cui faccio parte gli sviluppatori utilizzano sempre test unitari e revisioni del codice.In aggiunta a ciò, cerco anche di assicurarmi di non incorporare codice che le persone Maggio uso: tendo a scrivere codice solo per l'insieme di metodi di base richiesti affinché l'oggetto in questione funzioni come è stato specificato.Ho scoperto che incorporare metodi che potrebbero non essere mai utilizzati, ma che forniscono funzionalità, può introdurre involontariamente una "backdoor" o un utilizzo non intenzionale/imprevisto nel sistema.

È molto più semplice tornare indietro più tardi e introdurre metodi, attributi e proprietà richiesti piuttosto che anticipare qualcosa che potrebbe non arrivare mai.

Consiglierei di essere sulla difensiva per i dati che entrano in un "componente" o in un framework.All'interno di un "componente" o di un quadro si dovrebbe pensare che i dati siano "corretti".

Pensando in questo modo.Spetta al chiamante fornire i parametri corretti altrimenti TUTTE le funzioni e i metodi devono controllare ogni parametro in entrata.Ma se il controllo viene effettuato solo per il chiamante, il controllo è necessario solo una volta.Quindi, un parametro dovrebbe essere "corretto" e quindi può essere passato ai livelli inferiori.

  1. Controlla sempre i dati provenienti da fonti esterne, utenti ecc
  2. Un "componente" o un framework dovrebbe sempre controllare le chiamate in entrata.

Se è presente un bug e in una chiamata viene utilizzato un valore errato.Qual è davvero la cosa giusta da fare?Uno ha solo un'indicazione che i "dati" su cui sta lavorando il programma sono sbagliati e ad alcuni piace ASSERTS ma altri vogliono utilizzare la segnalazione avanzata degli errori e il possibile ripristino degli errori.In ogni caso i dati risultano errati e in alcuni casi è bene continuare a lavorarci sopra.(nota che va bene se almeno i server non muoiono)

Un'immagine inviata da un satellite potrebbe essere un caso in cui provare il ripristino avanzato degli errori su... un'immagine scaricata da Internet per visualizzare un'icona di errore per...

Consiglio alle persone di scrivere codice che sia fascista nell'ambiente di sviluppo e benevolo nella produzione.

Durante lo sviluppo è necessario individuare dati/logica/codice errati il ​​prima possibile per evitare che i problemi passino inosservati o causino problemi successivi in ​​cui la causa principale è difficile da monitorare.

Nella produzione gestisci i problemi nel modo più elegante possibile.Se qualcosa è davvero un errore irreversibile, gestiscilo e presenta tale informazione all'utente.

Ad esempio ecco il nostro codice per normalizzare un vettore.Se gli dai dati errati in fase di sviluppo urlerà, in produzione restituisce un valore di sicurezza.

inline const Vector3 Normalize( Vector3arg vec )
{
    const float len = Length(vec);
    ASSERTMSG(len > 0.0f "Invalid Normalization");
    return len == 0.0f ? vec : vec / len;
}

Lavoro sempre per prevenire cose come gli attacchi di iniezione.Tuttavia, quando lavori su un sito Intranet interno, la maggior parte delle funzionalità di sicurezza sembrano uno sforzo inutile.Li faccio ancora, forse semplicemente non così bene.

Bene, esiste un certo insieme di migliori pratiche per la sicurezza.Come minimo, per le applicazioni di database, è necessario fare attenzione a SQL Injection.

Altre cose come l'hashing delle password, la crittografia delle stringhe di connessione, ecc.sono anche uno standard.

Da qui in poi dipende dall'applicazione effettiva.

Fortunatamente, se lavori con framework come .Net, molta protezione di sicurezza è integrata.

Devi sempre programmare in modo difensivo, direi anche per le app interne, semplicemente perché gli utenti potrebbero, per pura fortuna, scrivere qualcosa che rompe la tua app.Certo, probabilmente non devi preoccuparti di provare a truffarti, ma comunque.Programma sempre in modo difensivo e presumi che l'app fallirà.

Usare il Test Driven Development aiuta sicuramente.Scrivi un singolo componente alla volta e quindi enumeri tutti i potenziali casi per gli input (tramite test) prima di scrivere il codice.Ciò garantisce di aver coperto tutte le basi e di non averne scritta alcuna Freddo codice che nessuno utilizzerà ma che potrebbe rompersi.

Anche se non faccio nulla di formale, in genere passo un po' di tempo esaminando ogni lezione e assicurandomi che:

  1. se sono in uno stato valido, rimangano in uno stato valido
  2. non è possibile costruirli in uno stato non valido
  3. In circostanze eccezionali falliranno nel modo più elegante possibile (spesso si tratta di una pulizia e di un lancio)

Dipende.

Se sto davvero inventando qualcosa per uso personale, allora scriverò il codice migliore a cui non devo pensare.Lascia che il compilatore sia mio amico per avvisi, ecc.ma non creerò automaticamente i tipi per il gusto di farlo.

Più è probabile che il codice venga utilizzato, anche occasionalmente, aumento il livello dei controlli.

  • numeri magici minimi
  • nomi di variabili migliori
  • lunghezze di array/stringhe completamente controllate e definite
  • programmazione mediante asserzioni contrattuali
  • controlli del valore nullo
  • eccezioni (a seconda del contesto del codice)
  • commenti esplicativi di base
  • documentazione di utilizzo accessibile (se perl ecc.)

Prenderò una definizione diversa di programmazione difensiva, come quella sostenuta da Java efficace di Josh Bloch.Nel libro, parla di come gestire gli oggetti mutabili che i chiamanti passano al tuo codice (ad esempio, nei setter) e gli oggetti mutabili che passi ai chiamanti (ad esempio, nei getter).

  • Per i setter, assicurati di clonare qualsiasi oggetto mutabile e di archiviare il clone.In questo modo, i chiamanti non possono modificare l'oggetto passato dopo il fatto per interrompere le invarianti del programma.
  • Per i getter, restituisci una vista immutabile dei tuoi dati interni, se l'interfaccia lo consente;oppure restituire un clone dei dati interni.
  • Quando si chiamano callback forniti dall'utente con dati interni, inviare una vista immutabile o un clone, a seconda dei casi, a meno che non si intenda che il callback alteri i dati, nel qual caso è necessario convalidarli a posteriori.

Il messaggio da portare a casa è assicurarsi che nessun codice esterno possa contenere un alias per qualsiasi oggetto mutabile che usi internamente, in modo da poter mantenere i tuoi invarianti.

Sono fermamente convinto che una corretta programmazione proteggerà da questi rischi.Cose come evitare le funzioni deprecate, che (almeno nelle librerie Microsoft C++) sono comunemente deprecate a causa di vulnerabilità della sicurezza, e convalidare tutto ciò che attraversa un confine esterno.

Le funzioni chiamate solo dal codice non dovrebbero richiedere un'eccessiva convalida dei parametri perché sei tu a controllare il chiamante, ovvero non viene oltrepassato alcun limite esterno.Le funzioni chiamate dal codice di altre persone dovrebbero presupporre che i parametri in entrata ad un certo punto non saranno validi e/o dannosi.

Il mio approccio nel gestire le funzioni esposte è semplicemente quello di bloccarmi, con un messaggio utile, se possibile.Se il chiamante non riesce a ottenere i parametri corretti, il problema è nel suo codice e dovrebbe risolverlo, non tu.(Ovviamente hai fornito la documentazione per la tua funzione, poiché è esposta.)

L'iniezione di codice è un problema solo se l'applicazione è in grado di elevare l'utente corrente.Se un processo può inserire codice nella tua applicazione, potrebbe facilmente scrivere il codice in memoria ed eseguirlo comunque.Senza la possibilità di ottenere pieno accesso al sistema, gli attacchi di code injection sono inutili.(Questo è il motivo per cui le applicazioni utilizzate dagli amministratori non dovrebbero essere scrivibili da utenti minori.)

Nella mia esperienza, impiegare positivamente la programmazione difensiva non significa necessariamente che si finisce per migliorare la qualità del proprio codice.Non fraintendetemi, è necessario programmare in modo difensivo per individuare il tipo di problemi che gli utenti incontreranno - agli utenti non piace quando il programma si blocca su di loro - ma è improbabile che ciò renda il codice più semplice da mantenere, prova, ecc.

Diversi anni fa, abbiamo stabilito come politica l'utilizzo di asserzioni a tutti i livelli del nostro software e questo, insieme ai test unitari, alle revisioni del codice, ecc.oltre alle nostre suite di test applicativi esistenti, hanno avuto un effetto significativo e positivo sulla qualità del nostro codice.

Java, JAR firmati e JAAS.

Java per prevenire l'overflow del buffer e gli exploit di puntatore/stack.

Non utilizzare JNI.(Java Native Interface) ti espone a librerie DLL/condivise.

JAR firmati per impedire che il caricamento delle classi costituisca un problema di sicurezza.

JAAS può consentire alla tua applicazione di non fidarsi di nessuno, nemmeno di se stessa.

J2EE ha un supporto integrato (certamente limitato) per la sicurezza basata sui ruoli.

C'è un po' di sovraccarico per alcuni di questi, ma le falle di sicurezza scompaiono.

Risposta semplice: Dipende.Troppa codifica difensiva Potere causare grossi problemi di prestazioni.

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