Quali sono le migliori funzioni di sanificazione dell'input PHP?


161

Sto provando a trovare una funzione che posso passare attraverso tutte le mie stringhe per disinfettare. In modo che la stringa che ne esce sia sicura per l'inserimento del database. Ma ci sono così tante funzioni di filtro là fuori che non sono sicuro di quali dovrei usare / bisogno.

Aiutatemi a riempire gli spazi vuoti:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}

4
per l'inserimento, va bene disinfettare contro l'iniezione SQL usando mysql_real_escape_string. È quando usi i dati SELEZIONATI (nell'output html o in una formula / funzione php) che dovresti applicare htmlentities
davidosomething,

Vedere stackoverflow.com/questions/60174/… per una risposta specifica alla pulizia per l'inserimento del database (fornisce un esempio di DOP, che altri hanno citato di seguito).
Pat

Risposte:


433

Fermare!

Stai commettendo un errore qui. Oh, no, hai scelto le giuste funzioni PHP per rendere i tuoi dati un po 'più sicuri. Va bene. Il tuo errore è nell'ordine delle operazioni e come e dove utilizzare queste funzioni.

È importante comprendere la differenza tra la sanificazione e la convalida dei dati utente, la fuga dei dati per l'archiviazione e la fuga dei dati per la presentazione.

Disinfezione e convalida dei dati utente

Quando gli utenti inviano dati, devi assicurarti che abbiano fornito qualcosa che ti aspetti.

Sanificazione e filtraggio

Ad esempio, se si prevede un numero, assicurarsi che i dati inviati siano un numero . Puoi anche trasmettere i dati utente in altri tipi. Tutto ciò che viene inviato viene inizialmente trattato come una stringa, quindi forzare i dati numerici noti a diventare numeri interi o float rende la sanificazione rapida e indolore.

Che dire di campi di testo in formato libero e textareas? Devi assicurarti che non ci sia nulla di inaspettato in quei campi. Principalmente, è necessario assicurarsi che i campi che non dovrebbero avere alcun contenuto HTML non contengano effettivamente HTML. Esistono due modi per affrontare questo problema.

Innanzitutto, puoi provare a sfuggire all'input HTML con htmlspecialchars. Non dovresti usare htmlentitiesper neutralizzare l'HTML, poiché eseguirà anche la codifica di caratteri accentati e di altri caratteri che ritiene debbano essere codificati.

In secondo luogo, puoi provare a rimuovere qualsiasi HTML possibile. strip_tagsè semplice e veloce, ma anche sciatto. HTML Purifier fa un lavoro molto più completo sia eliminando tutto l'HTML sia consentendo una whitelist selettiva di tag e attributi.

Le moderne versioni di PHP vengono fornite con l'estensione del filtro , che fornisce un modo completo per disinfettare l'input dell'utente.

Validazione

Assicurarsi che i dati inviati siano privi di contenuti imprevisti è solo metà del lavoro. Devi anche provare e assicurarti che i dati inviati contengano valori su cui puoi effettivamente lavorare.

Se ti aspetti un numero compreso tra 1 e 10, devi verificare quel valore. Se stai utilizzando uno di quei nuovi fantasiosi input numerici dell'era HTML5 con uno spinner e passaggi, assicurati che i dati inviati siano in linea con il passaggio.

Se i dati provengono da quello che dovrebbe essere un menu a discesa, assicurarsi che il valore inviato sia quello visualizzato nel menu.

Che dire degli input di testo che soddisfano altre esigenze? Ad esempio, gli input di data devono essere convalidati tramite strtotimela classe DateTime . La data indicata deve essere compresa tra gli intervalli previsti. E gli indirizzi e-mail? L' estensione di filtro menzionata in precedenza può verificare che un indirizzo sia ben formato, anche se sono un fan della libreria is_email .

Lo stesso vale per tutti gli altri controlli dei moduli. Hai i pulsanti di opzione? Convalida rispetto all'elenco. Hai delle caselle di controllo? Convalida rispetto all'elenco. Hai un caricamento di file? Assicurarsi che il file sia del tipo previsto e trattare il nome file come dati utente non filtrati.

Ogni browser moderno viene fornito con un set completo di strumenti per sviluppatori integrato, il che rende banale per chiunque manipolare il tuo modulo. Il tuo codice dovrebbe presumere che l'utente abbia rimosso completamente tutte le restrizioni sul lato client sul contenuto del modulo !

Escaping dei dati per l'archiviazione

Ora che ti sei assicurato che i tuoi dati siano nel formato previsto e contengano solo i valori previsti, devi preoccuparti di conservare tali dati nella memoria.

Ogni singolo meccanismo di archiviazione dei dati ha un modo specifico per assicurarsi che i dati siano correttamente salvati e codificati. Se stai creando SQL, il modo accettato per passare i dati nelle query è attraverso istruzioni preparate con segnaposto .

Uno dei modi migliori per lavorare con la maggior parte dei database SQL in PHP è l' estensione PDO . Segue il modello comune di preparazione di un'istruzione , associazione delle variabili all'istruzione , quindi invio dell'istruzione e delle variabili al server . Se non hai mai lavorato con PDO prima, ecco un tutorial orientato a MySQL piuttosto buono .

Alcuni database SQL hanno le loro estensioni speciali in PHP, tra cui SQL Server , PostgreSQL e SQLite 3 . Ognuna di queste estensioni ha preparato il supporto delle istruzioni che opera nello stesso modo preparazione-esecuzione-rilegatura del PDO. A volte potrebbe essere necessario utilizzare queste estensioni anziché PDO per supportare funzionalità o comportamenti non standard.

MySQL ha anche le sue estensioni PHP. Due di loro, in effetti. Vuoi usare sempre solo quello chiamato mysqli . La vecchia estensione "mysql" è stata deprecata e non è sicura o sana da usare nell'era moderna.

Personalmente non sono un fan di mysqli. Il modo in cui esegue l'associazione variabile su dichiarazioni preparate non è flessibile e può essere una seccatura da usare. In caso di dubbi, utilizzare invece DOP.

Se non si utilizza un database SQL per archiviare i dati, consultare la documentazione per l'interfaccia del database in uso per determinare come passare i dati in modo sicuro attraverso di esso.

Quando possibile, assicurarsi che il database memorizzi i dati in un formato appropriato. Memorizza i numeri in campi numerici. Memorizza le date nei campi data. Conservare i soldi in un campo decimale, non in un campo a virgola mobile. Rivedere la documentazione fornita dal database su come archiviare correttamente diversi tipi di dati.

Escaping dei dati per la presentazione

Ogni volta che mostri dati agli utenti, devi assicurarti che i dati siano salvati in modo sicuro, a meno che tu non sappia che non dovrebbero essere salvati.

Quando si emette HTML, è necessario passare quasi sempre tutti i dati originariamente forniti dall'utente htmlspecialchars. In effetti, l'unica volta che non dovresti farlo è quando sai che l'utente ha fornito HTML e che sai che è già stato disinfettato usando una whitelist.

A volte è necessario generare alcuni Javascript utilizzando PHP. Javascript non ha le stesse regole di escape dell'HTML! Un modo sicuro per fornire valori forniti dall'utente a Javascript tramite PHP è attraverso json_encode.

E altro ancora

Esistono molte altre sfumature nella convalida dei dati.

Ad esempio, la codifica del set di caratteri può essere un'enorme trappola . La tua applicazione dovrebbe seguire le pratiche descritte in " UTF-8 fino in fondo ". Esistono attacchi ipotetici che possono verificarsi quando si trattano i dati di stringa come un set di caratteri errato.

In precedenza ho menzionato gli strumenti di debug del browser. Questi strumenti possono anche essere utilizzati per manipolare i dati dei cookie. I cookie devono essere trattati come input dell'utente non attendibile .

La convalida e l'escape dei dati sono solo un aspetto della sicurezza delle applicazioni web. È necessario essere consapevoli delle metodologie di attacco delle applicazioni Web in modo da poter creare difese contro di esse.


E quando lo specifichi, assicurati che sia nell'elenco delle codifiche supportate.
Charles,

3
E non usare affatto htmlentities, sostituiscilo con htmlspecialchars allo scopo di sostituire solo <>, non tutti i personaggi alla sua entità
Your Common Sense

6
Assicurati di non chiamare htmlspecialcharsdue volte, perché ne parla nella parte "Quando gli utenti inviano i dati" e nella parte "Quando visualizzano i dati".
Savageman,

2
Upvoted. La risposta più utile che ho letto da molte domande e risposte relative a SQL Injection.
akinuri,

Assolutamente una risposta di qualità con molte spiegazioni e collegamenti per i futuri utenti di esplorare più opzioni. Anche io ho un riscontro da parte mia ...
James Walker il

32

La sanificazione più efficace per prevenire l'iniezione di SQL è l'utilizzo della parametrizzazione PDO. Utilizzando query con parametri, la query viene separata dai dati, in modo da rimuovere la minaccia dell'iniezione SQL del primo ordine.

In termini di rimozione di HTML, strip_tagsè probabilmente la migliore idea per rimuovere HTML, poiché rimuoverà semplicemente tutto. htmlentitiesfa quello che sembra, quindi funziona anche. Se è necessario analizzare quale HTML consentire (ovvero, si desidera consentire alcuni tag), è necessario utilizzare un parser esistente maturo come Purificatore HTML


2
Oh amico, ho scritto quel gigantesco muro di testo solo perché non ho visto nessuno menzionare Purificatore HTML, e qui mi hai battuto per circa 40 minuti. ;)
Charles,

3
Non dovresti spogliare l'HTML solo sull'output? IMO non dovresti mai cambiare i dati di input - non sai mai quando ne avrai bisogno
Joe Phillips,

11

Database Input - Come prevenire l'iniezione SQL

  1. Verificare che i dati di tipo intero, ad esempio, siano validi assicurandosi che siano effettivamente numeri interi
    • Nel caso di non stringhe è necessario assicurarsi che i dati siano effettivamente del tipo corretto
    • Nel caso di stringhe è necessario assicurarsi che la stringa sia racchiusa tra virgolette nella query (ovviamente, altrimenti non funzionerebbe nemmeno)
  2. Immettere il valore nel database evitando l'iniezione SQL (mysql_real_escape_string o query con parametri)
  3. Quando recuperi il valore dal database, assicurati di evitare attacchi Cross Site Scripting assicurandoti che non sia possibile iniettare HTML nella pagina (htmlspecialchars)

È necessario evitare l'input dell'utente prima di inserirlo o aggiornarlo nel database. Ecco un modo più vecchio per farlo. Ora vorrai utilizzare le query con parametri (probabilmente dalla classe PDO).

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

Output dal database - Come prevenire XSS (Cross Site Scripting)

Utilizzare htmlspecialchars()solo quando si emettono dati dal database. Lo stesso vale per HTML Purifier. Esempio:

$html['username'] = htmlspecialchars($clean['username'])

E infine ... quello che hai richiesto

Devo sottolineare che se usi oggetti PDO con query parametrizzate (il modo corretto di farlo), non c'è davvero un modo semplice per farlo facilmente. Ma se usi il vecchio modo "mysql", questo è ciò di cui avresti bisogno.

function filterThis($string) {
    return mysql_real_escape_string($string);
}

5

I miei 5 centesimi.

Nessuno qui capisce come mysql_real_escape_stringfunziona. Questa funzione non filtra o "disinfetta" nulla.
Pertanto, non è possibile utilizzare questa funzione come filtro universale che le salverà dall'iniezione.
Puoi usarlo solo quando capisci come funziona e dove applicabile.

Ho la risposta alla domanda molto simile che ho già scritto: in PHP quando invio stringhe al database dovrei occuparmi di caratteri illegali usando htmlspecialchars () o usare un'espressione regolare?
Fare clic per la spiegazione completa per la sicurezza lato database.

Per quanto riguarda le htmlentities - Charles ha ragione a dirti di separare queste funzioni.
Immagina di inserire dei dati, generati dall'amministratore, a cui è consentito pubblicare HTML. la tua funzione lo rovinerà.

Anche se consiglierei contro htmlentities. Questa funzione è diventata obsoleta molto tempo fa. Se si desidera sostituire solo <, >e "caratteri per motivi di sicurezza HTML, utilizzare la funzione sviluppata intenzionalmente a tale scopo, una htmlspecialchars () .


1
mysql_real_escape_stringsfugge ai caratteri necessari all'interno di una stringa. Non è strettamente il filtro o la sanificazione, ma racchiudere una stringa tra virgolette non lo è (e tutti lo fanno, praticamente non ho mai visto una domanda al riguardo). Quindi nulla viene sanificato quando scriviamo SQL? Ovviamente no. Ciò che impedisce l'iniezione SQL è l'uso di mysql_real_escape_string. Anche le virgolette che lo racchiudono, ma lo fanno tutti, e se provi quello che fai, finisci con un errore di sintassi SQL con questa omissione. La vera parte pericolosa viene gestita mysql_real_escape_string.
Savageman,

@Savageman scusa amico, non capisci niente. Non capisci come funziona mysql_real_escape_string. Questi "caratteri necessari" SONO citazioni. Non questa funzione né le citazioni da sole sanificano nulla. Queste 2 cose funzionano solo insieme . Rendere la stringa di query solo sintatticamente corretta, non "sicura dall'iniezione". E quale errore di sintassi otterrei solo per WHERE id = 1? ;)
Il tuo senso comune

Prova WHERE my_field = two words(senza virgolette) per ottenere l'errore di sintassi. Il tuo esempio è negativo perché non ha bisogno di virgolette né di escape, solo un controllo numerico. Inoltre non ho detto che le citazioni fossero inutili. Ho detto che tutti li usano, quindi questa non è la fonte di problemi riguardanti l'iniezione SQL.
Savageman,

1
@Savageman così, che ho detto: puoi usarlo solo quando capisci come funziona e dove applicabile. Hai appena ammesso che mysql_real_escape_string non è applicabile ovunque. Per quanto riguarda everyone use thempuoi controllare i codici qui su SO. Molte persone non usano le virgolette con i numeri. Vai a capire. Per favore, tieni presente che non sto discutendo qui quello che hai detto e che non lo fai. Sto solo spiegando le regole di sicurezza di base del database. Faresti meglio a imparare invece di discutere a vuoto. Nessuno ha menzionato citazioni o casting qui, ma m_r_e_s solo come se fosse magico. Di cosa sto parlando
tuo senso comune,

1
uno in alto, così come @Charles. Come principiante, l'interazione con il database ... rendere le cose sicure per input e display, Caratteri speciali, problemi di iniezione, è stata una curva di apprendimento molto ripida. Leggere il tuo post e il suo (così come le altre tue risposte PHP ad altre domande, mi ha aiutato molto. Grazie per tutto il tuo contributo.
James Walker

2

Per l'inserimento del database, tutto ciò che serve è mysql_real_escape_string(o utilizzare query con parametri). In genere non si desidera modificare i dati prima di salvarli, che è ciò che accadrebbe se si utilizzassero htmlentities. Ciò porterebbe a un pasticcio confuso in seguito quando lo avresti htmlentitiesripetuto per visualizzarlo da qualche parte su una pagina web.

Utilizzare htmlentitiesquando si stanno visualizzando i dati su una pagina Web da qualche parte.

In qualche modo correlato, se si inviano dati inviati da qualche parte in un'e-mail, ad esempio con un modulo di contatto, assicurarsi di rimuovere le righe da qualsiasi dato che verrà utilizzato nell'intestazione (come il Da: nome e indirizzo e-mail, oggetto secondario, ecc. )

$input = preg_replace('/\s+/', ' ', $input);

Se non lo fai, è solo questione di tempo prima che i robot spam trovino il tuo modulo e lo abusino, ho imparato a mie spese.



2

Dipende dal tipo di dati che stai utilizzando. Il migliore in generale da usare sarebbemysqli_real_escape_string ma, ad esempio, sai che non ci sarà contenuto HTML, l'uso di strip_tags aggiungerà ulteriore sicurezza.

Puoi anche rimuovere i personaggi che sai non dovrebbero essere ammessi.


1

Consiglio sempre di utilizzare un piccolo pacchetto di convalida come GUMP: https://github.com/Wixel/GUMP

Costruisci tutte le tue funzioni di base attorno a una libreria come questa ed è quasi impossibile dimenticare i servizi igienico-sanitari. "mysql_real_escape_string" non è la migliore alternativa per un buon filtraggio (come spiegato "Il tuo senso comune") - e se ti dimentichi di usarlo solo una volta, l'intero sistema sarà attaccabile attraverso iniezioni e altri brutte aggressioni.


1

Per tutti coloro che parlano e fanno affidamento su mysql_real_escape_string, è necessario notare che quella funzione è stata deprecata su PHP5 e non esiste più su PHP7.

IMHO il modo migliore per eseguire questa attività è utilizzare query parametrizzate tramite l'uso di PDO per interagire con il database. Controlla questo: https://phpdelusions.net/pdo_examples/select

Utilizzare sempre i filtri per elaborare l'input dell'utente. Vedi http://php.net/manual/es/function.filter-input.php


Questo in realtà non risponde alla domanda. Valuta di modificare la tua risposta per includere una soluzione.
Kris,

Spero ti piaccia!
Kuntur,

Lo voglio. Bella risposta!
Kris,

Suggerisco di notare che in PHP 7 mysqli_real_escape_string()è disponibile.
Chris,

Ciao Chris, le soluzioni qui esposte fanno riferimento a mysql_real_escape_string, ho notato chi ha letto d'ora in poi che non esiste più su PHP7 e ha proposto un'alternativa usando PDO (e filtri) non mysqli. Sentiti libero di aggiungere una nota che spiega una soluzione usando ciò che suggerisci. Saluti
Kuntur il

0

Si utilizza mysql_real_escape_string () nel codice simile al seguente.

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

Come dice la documentazione, il suo scopo è quello di evitare caratteri speciali nella stringa passata come argomento, tenendo conto del set di caratteri corrente della connessione in modo che sia sicuro inserirlo in un mysql_query () . La documentazione aggiunge inoltre:

Se devono essere inseriti dati binari, è necessario utilizzare questa funzione.

htmlentities () viene utilizzato per convertire alcuni caratteri nelle entità, quando si genera una stringa nel contenuto HTML.


0

Questo è uno dei modi in cui attualmente mi esercito,

  1. Impianto csrf e token tentazione sale insieme alla richiesta che deve essere effettuata dall'utente e convalidarli tutti insieme dalla richiesta. Fare riferimento qui
  2. assicurati di non fare troppo affidamento sui cookie lato client e assicurati di esercitarti ad utilizzare le sessioni lato server
  3. quando qualsiasi analisi dei dati, assicurarsi di accettare solo il tipo di dati e il metodo di trasferimento (come POST e GET)
  4. Assicurati di utilizzare SSL per la tua webApp / App
  5. Assicurati di generare anche una richiesta di sessione in base al tempo per limitare intenzionalmente la richiesta di spam.
  6. Quando i dati vengono analizzati sul server, assicurarsi di convalidare la richiesta che deve essere effettuata nel modello di dati desiderato, come json, html, ecc ... e quindi procedere
  7. sfuggire a tutti gli attributi illegali dall'input usando il tipo di escape ... come realescapestring.
  8. successivamente verifica solo il formato pulito del tipo di dati che desideri dall'utente.
    Esempio:
    - Email: controlla se l'input è in un formato email valido
    - text / string: Controlla solo che l'input sia solo in formato testo (string)
    - numero: controlla che sia consentito solo il formato numerico.
    - ecc. Pelase si riferisce alla libreria di convalida dell'input php dal portale php
    - Una volta convalidato, si prega di procedere utilizzando l'istruzione / PDO SQL preparata.
    - Una volta fatto, assicurati di uscire e terminare la connessione
    - Non dimenticare di cancellare il valore di uscita una volta fatto.

Questo è tutto ciò che credo sia sufficiente per un secondo di base. Dovrebbe impedire a tutti i principali attacchi degli hacker.

Per la sicurezza lato server, potresti voler impostare il tuo apache / htaccess per la limitazione degli accessi e la prevenzione del robot e anche la prevenzione del routing. Ci sono molte cose da fare per la sicurezza lato server oltre alla parte del sistema sul lato server.

Puoi imparare e ottenere una copia del sec dal livello di htaccess apache sec (rpactices comuni)


0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}

0

che dire di questo

$string = htmlspecialchars(strip_tags($_POST['example']));

o questo

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.