Puoi hackerare il tuo sito? Uno sguardo ad alcune considerazioni essenziali sulla sicurezza

Due volte al mese, rivisitiamo alcuni dei post preferiti dei nostri lettori da tutta la storia di Nettuts +. Questo tutorial è stato pubblicato per la prima volta nel luglio 2008.

La versione uno diventa oro! I visitatori stanno arrivando da ogni angolo del globo. Sai che probabilmente ci saranno alcuni problemi iniziali; Voglio dire, questo è 1.0.0.0? tutti quegli zeri hanno lo scopo di concederci un po 'di grazia, giusto?

Forse quel foglio di stile vile non si sovrapporrà elegantemente sul browser X. Un commento incompleto tira fuori un markup rotto. Forse dovresti aver persistito quelle connessioni di database dopo tutto. Ehi, tutti noi trascuriamo le cose nell'eccitazione di far funzionare la nostra prima versione - ma quante di queste sviste possiamo tranquillamente addormentarci, e quante potrebbero lasciare solo un gusto amaro nelle nostre e più dolorosamente le bocche dei nostri clienti?

Questo articolo attraversa la fase di brainstorming della pianificazione per ciò che è, in questo caso, un'ipotetica applicazione web incentrata sull'utente.

Sebbene non resti un progetto completo, né una struttura pronta per il mercato, la mia speranza è che ognuno di voi, di fronte ai carichi di lavoro futuri, possa ispirarsi alle migliori pratiche descritte. Quindi, senza ulteriori indugi? Sei seduto comodamente?


L'esempio

Ci è stato chiesto dal nostro cliente di incorporare in un sito esistente, un sistema di revisione dei libri. Il sito ha già account utente e consente commenti anonimi.

Dopo una breve chat con il cliente, abbiamo le seguenti specifiche da implementare, e solo ventiquattro ore per farlo:

Nota: il server del client sta eseguendo PHP5 e MySQL, ma questi dettagli non sono fondamentali per comprendere i bugbags descritti in questo articolo.


I processi:

Il nostro cliente ci ha dato un PHP include per accedere al database:

In realtà non abbiamo bisogno della fonte di questo file per usarlo. In effetti, se il cliente ci avesse semplicemente detto dove viveva, avremmo potuto usarlo con una dichiarazione di inclusione e il $ db variabile.

Su autorizzazione? all'interno dello schema datatable ci occupiamo dei seguenti nomi di colonna:

  • nome utente, varchar (128): memorizzato come testo normale.
  • parola d'ordine, varchar (128): memorizzato come testo normale.

Dato che stiamo lavorando contro il tempo? scriviamo una funzione PHP il più rapidamente possibile che riusciamo a riutilizzare per autenticare i nostri utenti:


$ _REQUEST Variabili

Nel codice qui sopra noterai che ho evidenziato un'area ambra e un'area rossa.

Perché ho evidenziato il non così pericoloso $ _REQUEST variabili?

Sebbene ciò non esponga alcun pericolo reale, ciò che consente è un approccio lassista quando si tratta di codice lato client. PHP ha tre array che la maggior parte di noi usa per ottenere i dati inviati dagli utenti, e il più delle volte potremmo essere tentati di usare $ _REQUEST. Questo array fornisce convenientemente il nostro accesso PHP alle variabili POST e GET, ma qui sta un potenziale riaggancio?

Si consideri il seguente scenario. Scrivi il lato client del codice per utilizzare le richieste POST, ma passerai il progetto mentre ti prendi una pausa e quando torni, il tuo compagno ha scritto un paio di richieste GET nel progetto. Va tutto bene, ma non dovrebbe.

Poco dopo, un utente ignaro digita un link esterno in una casella di commento, e prima che tu lo sai, quel sito esterno ha una dozzina di combinazioni nome utente / password nel suo registro dei referrer.

Facendo riferimento al $ _POST variabili al posto di $ _REQUEST, noi eliminiamo accidentalmente pubblicare qualsiasi codice funzionante che potrebbe rivelare una richiesta GET rischiosa.

Lo stesso principio si applica agli identificatori di sessione. Se trovi che stai scrivendo variabili di sessione in URL, stai facendo qualcosa di sbagliato o hai un molto buona ragione per farlo.


SQL Injection

Riferendosi nuovamente al codice PHP, la linea rossa evidenziata potrebbe essere saltata fuori da qualcuno di voi? Per coloro che non hanno individuato il problema, ti darò un esempio e da lì, vedi se qualcosa ti colpisce come rischioso.

La protezione più rapida è quella di rimuovere il allegato personaggi o sfuggirli.

Questa immagine chiarisce il difetto nell'incorporare le variabili direttamente nelle istruzioni SQL. Anche se non si può dire esattamente che cosa controllo che un utente malintenzionato potrebbe avere - è garantito, se si utilizza questo metodo per mettere insieme un'istruzione SQL, che il server è a malapena protetto. L'esempio sopra è abbastanza pericoloso su un account di sola lettura; i poteri di una connessione di lettura / scrittura sono limitati solo dalla tua immaginazione.

La protezione dall'iniezione SQL è in realtà abbastanza semplice. Diamo prima un'occhiata al caso delle variabili stringa racchiuse tra virgolette:

La soluzione più rapida è rimuovere il allegato personaggi o sfuggirli. Dal PHP 4.3.0, la funzione mysql_real_escape_string è stato disponibile per pulire le stringhe in entrata. La funzione considera la stringa non elaborata come un singolo parametro e restituisce la stringa con i caratteri volatili sfuggiti. però mysql_real_escape_string non sfugge tutti i caratteri che sono caratteri di controllo validi in SQL? gli elementi evidenziati nell'immagine in basso mostrano le tecniche che uso per disinfettare String, Number e booleano valori.

Il primo punto saliente, la linea che imposta $ string_b usa una funzione PHP chiamata addcslashes. Questa funzione è stata parte di PHP dalla versione 4, e come è scritto nell'esempio precedente, è il mio metodo preferito per la salute e la sicurezza della stringa SQL.

Una grande quantità di informazioni è disponibile nella documentazione di PHP, ma spiegherò brevemente che cosa addcslashes fa e come differisce a mysql_real_escape_string.

Dal diagramma sopra puoi vedere quello mysql_real_escape_string non aggiunge barre al carattere percentuale (%).

Il % è usato in SQL PIACE clausole, così come alcune altre. Si comporta come un jolly e non un personaggio letterale. Quindi dovrebbe essere evaso da un carattere backslash precedente in tutti i casi in cui i letterali stringa costituiscono un'istruzione SQL.

Il secondo parametro, io passo a addcslashes, quale nell'immagine è grassetto; è il gruppo di caratteri che PHP aggiungerà alle barre per. Nella maggior parte dei casi, lo farà Diviso la stringa che fornisci personaggi, e quindi operare su ciascuno. Vale la pena notare che questo gruppo di caratteri può anche essere alimentato con una serie di caratteri, sebbene ciò esuli dallo scopo di questo articolo. Negli scenari di cui stiamo discutendo, possiamo utilizzare letteralmente caratteri alfanumerici, ad es. ? Abcd1234? e tutti gli altri caratteri come i loro letterali in stile C? \ r \ n \ t ?, o il loro indice ASCII? \ x0A \ x0D \ x09?.

Il prossimo evidenziare rende i nostri valori numerici sicuri per le istruzioni SQL.

Questa volta non vogliamo sottrarre nulla, vogliamo solo avere un valore numerico valido, sia esso un intero o un punto mobile.

Avresti potuto notare linea 10, e forse si chiedeva quale fosse lo scopo. Alcuni anni fa, ho lavorato su un sistema di registrazione del call center che stava usando variabile + = 0; per garantire valori numerici. Perché questo è stato fatto, non posso onestamente dire? a meno che prima di PHP 4 fosse così che l'abbiamo fatto ?! Forse qualcuno che legge può far luce sull'argomento. A parte questo, se tu, come ho fatto io, ti imbatti in una linea del genere in natura, saprai cosa sta cercando di fare.

Andando avanti allora; Linee 11 e 12 sono tutto ciò di cui abbiamo bisogno per preparare i nostri valori di input numerici per SQL. Dovrei dire, aveva la stringa di input $ number_i contenuto qualsiasi carattere non numerico di fronte o a sinistra di quelli numerici? i nostri valori $ NUMBER_A, $ number_b e $ number_c farebbe tutto uguale a 0.

Useremo floatval pulire i nostri numeri di input; PHP stampa solo le posizioni decimali quando esistono nel valore di input, quindi stamparle in un'istruzione SQL non causerà alcun errore se nell'input non è presente alcun decimale. Finché il nostro codice server è sicuro, possiamo lasciare la convalida più schizzinosa al nostro codice lato client.

Prima di passare a un elenco finale per il nostro PHP, daremo un'occhiata alla finale codice evidenziare, il pugilato booleano.

Come l'equivalente C ++, un booleano in PHP è davvero un numero intero. Come in, True + True = Due. Esistono innumerevoli modi per tradurre una stringa di input in un tipo booleano, il mio preferito è: la stringa in minuscolo contiene la parola true?

Tutti voi potreste avere i vostri metodi preferiti; la stringa di input è esplicitamente uguale? true? o è la stringa di input? 1? eccetera? l'importante è che il valore che arriva, qualunque esso sia, è rappresentato da un booleano (o intero) prima di usarlo.

La mia filosofia personale è semplicemente: se X è vero o falso, poi X è un booleano. Scriverò beatamente tutto il codice che potrei avere bisogno di rivedere più tardi con Booleans e non short, int, tinyint o qualsiasi cosa che non sia booleana. Quello che succede sul metallo non è la mia preoccupazione, quindi quello che sembra un essere umano è molto più importante.

Quindi, come con numeri e stringhe, i nostri booleani sono garantiti al sicuro dal momento in cui li inseriamo nella nostra sceneggiatura. Inoltre il nostro codice igienico non ha bisogno di linee aggiuntive.


Elaborazione HTML

Ora che abbiamo protetto il nostro SQL dalle iniezioni e abbiamo fatto in modo che solo un accesso POST possa lavorare in modo affiatato con il nostro script, siamo pronti a implementare la nostra funzione di invio delle recensioni.

Il nostro cliente desidera consentire agli utenti abilitati alla revisione di formattare i loro contributi come normali HTML. Questo sembrerebbe abbastanza semplice, ma sappiamo anche che gli indirizzi email sono dieci al centesimo, e gli account delle librerie sono creati a livello di codice, quindi nel migliore interesse di tutti ci assicureremo passano solo i tag che diciamo.

Decidere come verifichiamo la recensione in arrivo potrebbe sembrare scoraggiante. Le specifiche HTML hanno una serie piuttosto completa di tag, molti dei quali siamo felici di consentire.

Per quanto possa sembrare lungo il tempo, consiglio vivamente a tutti - scegli cosa permettere e mai cosa negare. Lingue di markup del browser e del server tutti aderire alla strutturazione XML, in modo che possiamo basare il nostro codice sul fatto fondamentale che il codice eseguibile deve essere circondato da, o essere parte di, tag angolato.

Certo, ci sono diversi modi in cui possiamo ottenere lo stesso risultato. Per questo articolo descriverò una possibile pipeline di espressioni regolari:

Queste espressioni regolari non produrranno risultati impeccabili, ma nella maggior parte dei casi dovrebbero svolgere un lavoro quasi elegante.

Diamo un'occhiata all'espressione regolare che useremo nel nostro PHP. Noterai che sono stati dichiarati due array. $ safelist_review e $ safelist_comment - questo è così che possiamo usare le stesse funzioni per convalidare recensioni e in seguito, commenti:

? ed ecco la funzione principale che chiameremo per disinfettare i dati di revisione e commento:

I parametri di input, ho evidenziato il rosso e il blu. $ input sono i dati non elaborati inviati dall'utente e $ lista è un riferimento alla matrice di espressioni; $ safelist_review o $ safelist_comment a seconda del tipo di invio che desideriamo validare.

La funzione restituisce la versione riformattata dei dati inviati - qualsiasi tag quello non passare una qualsiasi delle espressioni regolari nella nostra lista scelta vengono convertite in equivalenti codificati HTML. Che nei termini più semplici rende < e > in < e > anche altri personaggi sono modificati, ma nessuno di questi rappresenta una minaccia alla sicurezza per il nostro cliente o gli utenti.

Nota: le funzioni: cleanWhitespace e getTags siamo incluso nei file sorgente dell'articolo.

Sarebbe corretto presumere che tutto ciò che abbiamo veramente fatto sia aiutato a sopravvivere all'estetica delle pagine del nostro sito e non fatto tutto per proteggere la sicurezza dell'utente. Rimane comunque un buco di sicurezza piuttosto enorme: l'iniezione di JavaScript.

Questo difetto particolare potrebbe essere risolto con alcune espressioni più regolari e / o modifiche a quelle che stiamo già utilizzando. La nostra espressione regolare di ancoraggio consente solo? /? ?,? h? ? e ?#? ? valori come il href attributo - che in realtà è solo un esempio di soluzione. I browser, su tutta la linea, comprendono una grande varietà di script visibile attributi, come al clic, onLoad e così via.

In sostanza, abbiamo creato un problema spinoso per noi stessi. Volevamo consentire l'HTML, ma ora abbiamo un elenco pressoché infinito di parole chiave da eliminare. C'è ovviamente un modo meno che perfetto - ma abbastanza rapidamente scritto per farlo:

Riflettendoti saresti assolutamente giustificato nel chiedere, "Perché non usiamo solo BBCode o Textile o? ??

Per quanto mi riguarda, se avessi a che fare con l'elaborazione del mark-up, potrei persino optare per XML walking. Dopo tutti i dati in arrivo dovrebbero essere XML valido.

Tuttavia, questo articolo non ha lo scopo di insegnarci come regex, come PHP o come scrivere qualcosa in una lingua particolare. La logica alla base del semplice essere, non lasciare socchiuse le porte.

Quindi finiamo allora; con una rapida rassegna di ciò che abbiamo visto:

Certamente, questo articolo non ti ha fornito alcun progetto off-the-shelf. Uno degli scopi principali della mia scrittura non era quello di spaventare i progettisti che codificano, o snocciolare il lavoro dei programmatori da nessuna parte, ma di incoraggiare tutti a creare codice robusto sin dall'inizio. Detto questo, ho intenzione di rivisitare alcuni elementi di questo articolo in modo più dettagliato in seguito.

Fino ad allora, codifica sicura!