Come posso disinfettare l'input dell'utente con PHP?


1124

Esiste da qualche parte una funzione catchall che funziona bene per disinfettare l'input dell'utente per l'iniezione SQL e gli attacchi XSS, pur consentendo alcuni tipi di tag HTML?


42
Al giorno d'oggi, per evitare l'iniezione sql, usa PDO o MySQLi.
Francisco Presencia,

76
L'uso di DOP o MySQLi non è abbastanza. Se costruisci le tue istruzioni SQL con dati non attendibili, ad esempio select * from users where name='$name', non importa se usi PDO o MySQLi o MySQL. Sei ancora in pericolo. È necessario utilizzare query parametrizzate o, se necessario, utilizzare meccanismi di escape sui dati, ma è molto meno preferibile.
Andy Lester,

26
@AndyLester Stai insinuando che qualcuno usa la DOP senza dichiarazioni preparate? :)

64
Sto dicendo che "Usa DOP o MySQLi" non è abbastanza informazione per spiegare ai principianti come usarli in sicurezza. Tu ed io sappiamo che le dichiarazioni preparate contano, ma non presumo che chiunque leggerà questa domanda lo saprà. Ecco perché ho aggiunto le istruzioni esplicite.
Andy Lester,

30
Il commento di Andy è del tutto valido. Di recente ho convertito il mio sito Web mysql in DOP pensando di essere in qualche modo al sicuro dagli attacchi di iniezione. Fu solo durante il processo che mi resi conto che alcune delle mie istruzioni sql erano ancora costruite usando l'input dell'utente. Ho quindi risolto il problema utilizzando dichiarazioni preparate. Per un principiante completo, non è del tutto chiaro che esiste una distinzione poiché molti esperti lanciano il commento sull'uso della DOP ma non specificano la necessità di dichiarazioni preparate. Il presupposto è che questo è ovvio. Ma non per un principiante.
GhostRider,

Risposte:


1184

È un'idea sbagliata comune che l'input dell'utente possa essere filtrato. PHP ha persino una "caratteristica" (ora deprecata), chiamata virgolette , che si basa su questa idea. Non ha senso. Dimentica il filtro (o la pulizia, o come la chiamano le persone).

Quello che dovresti fare, per evitare problemi, è abbastanza semplice: ogni volta che incorpori una stringa in un codice esterno, devi evitarlo, secondo le regole di quella lingua. Ad esempio, se si incorpora una stringa in alcuni SQL destinati a MySQL, è necessario sfuggire alla stringa con la funzione di MySQL per questo scopo ( mysqli_real_escape_string). (O, nel caso di database, l'uso di istruzioni preparate è un approccio migliore, quando possibile.)

Un altro esempio è HTML: se si incorporano stringhe all'interno del markup HTML, è necessario evitarlo con htmlspecialchars. Ciò significa che ogni singola echoo printdichiarazione dovrebbe usare htmlspecialchars.

Un terzo esempio potrebbe essere rappresentato dai comandi di shell: se hai intenzione di incorporare stringhe (come argomenti) in comandi esterni e chiamarli con exec, allora devi usare escapeshellcmde escapeshellarg.

E così via e così via ...

L' unico caso in cui è necessario filtrare attivamente i dati è se si accetta input preformattato. Ad esempio, se consenti ai tuoi utenti di pubblicare markup HTML, prevedi di visualizzarli sul sito. Tuttavia, dovresti essere saggio per evitarlo a tutti i costi, poiché non importa quanto bene lo filtri, sarà sempre un potenziale buco di sicurezza.


245
"Ciò significa che ogni singola istruzione echo o print dovrebbe usare htmlspecialchars" - ovviamente, intendi "ogni ... istruzione che trasmette l'input dell'utente"; htmlspecialchars () - ifying "echo 'Hello, world!';" sarebbe pazzo;)
Bobby Jack,

10
C'è un caso in cui penso che il filtro sia la soluzione giusta: UTF-8. Non si desidera sequenze UTF-8 non valide in tutta l'applicazione (è possibile che si verifichino diversi errori di recupero in base al percorso del codice) e UTF-8 può essere facilmente filtrato (o rifiutato).
Kornel,

6
@jbyrd - no, LIKE usa un linguaggio regexp specializzato. Dovrai evitare la stringa di input due volte, una volta per la regexp e una volta per la codifica della stringa mysql. È codice all'interno del codice all'interno del codice.
troelskn,

6
In questo momento mysql_real_escape_stringè deprecato. Al giorno d'oggi è considerato una buona pratica utilizzare istruzioni preparate per prevenire l'iniezione di SQL. Quindi passa a MySQLi o PDO.
Marcel Korpel,

4
Perché limiti la superficie di attacco. Se si esegue la sanificazione precoce (durante l'immissione), è necessario essere certi che non vi siano altri buchi nell'applicazione in cui potrebbero penetrare dati errati. Considerando che se lo fai in ritardo, la tua funzione di output non deve "fidarsi" del fatto che vengano forniti dati sicuri - presuppone semplicemente che tutto non sia sicuro.
troelskn,

217

Non tentare di impedire l'iniezione SQL disinfettando i dati di input.

Invece, non consentire l'utilizzo dei dati nella creazione del codice SQL . Usa istruzioni preparate (ovvero utilizzando parametri in una query modello) che utilizza variabili associate. È l'unico modo per essere garantito contro l'iniezione SQL.

Per ulteriori informazioni sulla prevenzione dell'iniezione SQL, consultare il mio sito Web http://bobby-tables.com/ .


18
Oppure visita la documentazione ufficiale e impara la DOP e le dichiarazioni preparate. Piccola curva di apprendimento, ma se conosci abbastanza bene SQL, non avrai problemi ad adattarti.
un programmatore il

2
Per il caso specifico di SQL Injection, questa è la risposta corretta!
Scott Arciszewski,

4
Si noti che le istruzioni preparate non aggiungono alcuna sicurezza, come invece fanno le query con parametri. Sono semplicemente molto facili da usare insieme in PHP.
Base

Non è l'unico modo garantito. Anche l'esagono dell'input e unhex nella query impediranno. Inoltre, gli attacchi esadecimali non sono possibili se usi esagono a destra.
Ramon Bakker,

E se stai inserendo qualcosa di specializzato, come indirizzi e-mail o nomi utente?
Abraham Brookes,

79

No. Non è possibile filtrare genericamente i dati senza alcun contesto a cosa servono. A volte vorresti prendere una query SQL come input e a volte vorresti prendere HTML come input.

Devi filtrare l'input su una whitelist: assicurati che i dati corrispondano ad alcune specifiche di ciò che ti aspetti. Quindi è necessario evitarlo prima di utilizzarlo, a seconda del contesto in cui lo si utilizza.

Il processo di escape dei dati per SQL - per impedire l'iniezione SQL - è molto diverso dal processo di escape dei dati per (X) HTML, per impedire XSS.


52

PHP ha ora le nuove simpatiche funzioni filter_input, che per esempio ti liberano dal trovare "il massimo regex della posta elettronica" ora che esiste un tipo FILTER_VALIDATE_EMAIL incorporato

La mia classe di filtri (utilizza JavaScript per evidenziare i campi difettosi) può essere avviata tramite una richiesta Ajax o un normale post di modulo. (vedi l'esempio sotto)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

Naturalmente, tieni presente che è necessario eseguire l'escape della query sql anche a seconda del tipo di db che stai utilizzando (mysql_real_escape_string () è inutile per un server sql, ad esempio). Probabilmente vuoi gestirlo automaticamente al tuo livello di applicazione appropriato come un ORM. Inoltre, come menzionato sopra: per l'output in html usa le altre funzioni php dedicate come htmlspecialchars;)

Per consentire davvero l'input HTML con classi e / o tag simili come stripped dipende da uno dei pacchetti di validazione xss dedicati. NON SCRIVERE I TUOI SEGNI PROPRI A PARSE HTML!


18
Sembra che potrebbe essere uno script utile per la convalida degli input, ma è completamente irrilevante per la domanda.
rjmunro,

43

No non c'è.

Prima di tutto, l'iniezione SQL è un problema di filtraggio dell'input e XSS è un output che sfugge a uno - quindi non eseguiresti nemmeno queste due operazioni contemporaneamente nel ciclo di vita del codice.

Regole empiriche di base

  • Per query SQL, associare i parametri (come con PDO) o utilizzare una funzione di escape nativa del driver per variabili di query (come mysql_real_escape_string())
  • Utilizzare strip_tags()per filtrare HTML indesiderato
  • Esci da tutti gli altri output con htmlspecialchars()e fai attenzione ai parametri 2 ° e 3 ° qui.

1
Quindi usi strip_tags () o htmlspecialchars () quando sai che l'input ha HTML che vuoi liberare o scappare rispettivamente - non lo stai usando per scopi di sicurezza, giusto? Inoltre, quando fai il bind, cosa fa per cose come i tavoli Bobby? "Robert"); DROP TABLE Studenti; - "Sfugge alle virgolette?
Robert Mark Bram,

2
Se si dispone di dati utente che verranno inseriti in un database e successivamente visualizzati su pagine Web, di solito non viene letto molto più di quanto sia scritto? Per me, ha più senso filtrarlo una volta (come input) prima di memorizzarlo, invece di doverlo filtrare ogni volta che lo si visualizza. Mi sto perdendo qualcosa o un gruppo di persone ha votato per inutili prestazioni generali in questo e la risposta accettata?
jbo5112,

2
La migliore risposta per me È breve e risponde bene alla domanda se me lo chiedi. È possibile attaccare PHP in qualche modo tramite $ _POST o $ _GET con qualche iniezione o è impossibile?
Jo Smo,

eh si, gli array $ post e $ get accettano tutti i caratteri, ma alcuni di questi caratteri possono essere usati contro di te se il personaggio può essere elencato nella pagina php pubblicata. quindi se non sfuggi ai personaggi incapsulanti (come "," e `), potresti aprire un vettore di attacco. Il carattere` spesso viene perso e può essere usato per formare hack di esecuzione da riga di comando. I servizi igienico-sanitari impediranno l'hacking dell'input dell'utente, ma non ti aiuterà con gli hack del firewall delle applicazioni Web.
drtechno,

22

Per risolvere il problema XSS, dai un'occhiata a Purificatore HTML . È abbastanza configurabile e ha un discreto track record.

Per quanto riguarda gli attacchi di iniezione SQL, assicurati di controllare l'input dell'utente, quindi eseguilo attraverso mysql_real_escape_string (). La funzione non annullerà tutti gli attacchi di iniezione, quindi è importante controllare i dati prima di scaricarli nella stringa di query.

Una soluzione migliore è utilizzare dichiarazioni preparate. La libreria PDO e l'estensione mysqli supportano questi.


non esiste un "modo migliore" per fare qualcosa di simile alla sanificazione dell'input. Usa una libreria, il purificatore HTML è buono. Queste librerie sono state martellate molte volte. Quindi è molto più a prova di proiettile di qualsiasi cosa tu possa inventare da te
paan


Il problema con wordpress è che non è necessariamente un attacco di php-sql che provoca violazioni del database. Perdere plug-in programmati che memorizzano dati che una query xml rivela segreti è più problematico.
drtechno,


17

Un trucco che può aiutare nella specifica circostanza in cui hai una pagina simile /mypage?id=53e usi l'id in una clausola WHERE è quello di assicurarti che id sia sicuramente un numero intero, in questo modo:

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

Ma ovviamente questo elimina solo un attacco specifico, quindi leggi tutte le altre risposte. (E sì, lo so che il codice sopra non è eccezionale, ma mostra la difesa specifica.)


11
Uso invece $ id = intval ($ id) :)
Duc Tran il

La trasmissione di numeri interi è un buon modo per garantire l'inserimento di soli dati numerici.
prova il

1
$id = (int)$_GET['id']ed $que = sprintf('SELECT ... WHERE id="%d"', $id)è anche buono
vladkras il

16

Metodi per la disinfezione dell'input dell'utente con PHP:

  • Usa le versioni moderne di MySQL e PHP.

  • Imposta esplicitamente il set di caratteri:

    • $ Mysqli-> set_charset ( "utf8");
      Manuale
    • $ pdo = nuovo PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password);
      Manuale
    • $ pdo-> exec ("imposta nomi utf8");
      Manuale
    • $ pdo = nuovo DOP (
      "mysql: host = $ host; dbname = $ db", $ user, $ pass, 
      Vettore(
      DOP :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION,
      PDO :: MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      Manuale
    • mysql_set_charset ( 'utf8')
      [deprecato in PHP 5.5.0, rimosso in PHP 7.0.0].
  • Usa set di caratteri sicuri:

    • Seleziona utf8, latin1, ascii .., non usare caratteri vulnerabili big5, cp932, gb2312, gbk, sjis.
  • Usa la funzione spazializzata:

    • Dichiarazioni preparate da MySQLi:
      $ stmt = $ mysqli-> prepar ((SELEZIONA * DA test DOVE nome =? LIMIT 1 '); 
      $ param = "'OR 1 = 1 / *";
      $ stmt-> bind_param ('s', $ param);
      $ stmt-> execute ();
    • PDO :: quote () - posiziona le virgolette attorno alla stringa di input (se necessario) e sfugge ai caratteri speciali all'interno della stringa di input, usando uno stile di quotazione appropriato per il driver sottostante:

      $ pdo = nuovo PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); set esplicito il set di caratteri
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); disabilita l'emulazione delle istruzioni preparate per evitare il fallback delle emulazioni delle dichiarazioni che MySQL non può preparare in modo nativo (per prevenire l'iniezione)
      $ var = $ pdo-> quote ("'OR 1 = 1 / *"); non solo sfugge al letterale, ma lo cita anche (in caratteri a virgoletta singola) $ stmt = $ pdo-> query ("SELEZIONA * DA test DOVE nome = $ var LIMIT 1");

    • Dichiarazioni preparate DOP : vs le istruzioni preparate MySQLi supportano più driver di database e parametri denominati:

      $ pdo = nuovo PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); set esplicito il set di caratteri
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); disabilita l'emulazione delle istruzioni preparate per impedire il fallback delle emulazioni che MySQL non può preparare in modo nativo (per impedire l'iniezione) $ stmt = $ pdo-> prepara ('SELEZIONA * DA test DOVE nome =? LIMIT 1'); $ stmt-> execute (["'OR 1 = 1 / *"]);

    • mysql_real_escape_string [deprecato in PHP 5.5.0, rimosso in PHP 7.0.0].
    • mysqli_real_escape_string Esegue l'escape di caratteri speciali in una stringa da utilizzare in un'istruzione SQL, tenendo conto del set di caratteri corrente della connessione. Ma si consiglia di utilizzare le istruzioni preparate perché non sono semplicemente stringhe di escape, un'istruzione fornisce un piano di esecuzione delle query completo, che include quali tabelle e indici utilizzare, è un modo ottimizzato.
    • Usa virgolette singole ('') attorno alle tue variabili all'interno della tua query.
  • Verifica che la variabile contenga ciò che ti aspetti:

    • Se ti aspetti un numero intero, utilizza:
      ctype_digit - Controlla i caratteri numerici; 
      $ value = (int) $ value;
      $ value = intval ($ value);
      $ var = filter_var ('0755', FILTER_VALIDATE_INT, $ opzioni);
    • Per le stringhe usare:
      is_string () - Scopri se il tipo di una variabile è stringa

      Usa la funzione filtro filter_var () - filtra una variabile con un filtro specificato:
      $ email = filter_var ($ email, FILTER_SANITIZE_EMAIL); 
      $ newstr = filter_var ($ str, FILTER_SANITIZE_STRING);
      più filtri predefiniti
    • filter_input () - Ottiene una variabile esterna specifica per nome e facoltativamente la filtra:
      $ search_html = filter_input (INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () - Esegue una corrispondenza di espressioni regolari;
    • Scrivi la tua funzione di validazione.

11

Quello che stai descrivendo qui è due problemi separati:

  1. Disinfezione / filtro dei dati di input dell'utente.
  2. Uscita di escape.

1) L'input dell'utente dovrebbe sempre essere considerato cattivo.

Usare istruzioni preparate o / e filtrare con mysql_real_escape_string è sicuramente un must. PHP ha anche filter_input integrato in cui è un buon punto di partenza.

2) Questo è un argomento di grandi dimensioni e dipende dal contesto dei dati in uscita. Per HTML ci sono soluzioni come htmlpurifier là fuori. come regola generale, evita sempre qualsiasi cosa tu produca.

Entrambi i problemi sono troppo grandi per essere inseriti in un singolo post, ma ci sono molti post che vanno più in dettaglio:

Metodi di output PHP

Uscita PHP più sicura


9

Se stai usando PostgreSQL, l'input da PHP può essere evitato con pg_escape_string ()

 $username = pg_escape_string($_POST['username']);

Dalla documentazione ( http://php.net/manual/es/function.pg-escape-string.php ):

pg_escape_string () esegue l'escape di una stringa per l'interrogazione del database. Restituisce una stringa con escape nel formato PostgreSQL senza virgolette.


1
pg_escape_literal () è la funzione consigliata da utilizzare per PostgreSQL.
criptico ツ

8

Non esiste una funzione catchall, perché ci sono più preoccupazioni da affrontare.

  1. SQL Injection - Oggi, in generale, ogni progetto PHP dovrebbe utilizzare istruzioni preparate tramite PHP Data Objects (PDO) come best practice, prevenendo un errore da una citazione vagante e una soluzione completa contro l'iniezione . È anche il modo più flessibile e sicuro per accedere al tuo database.

    Dai un'occhiata (l'unico vero) tutorial PDO per praticamente tutto ciò che devi sapere sul DOP. (Un grazie sincero al miglior collaboratore SO, @YourCommonSense, per questa fantastica risorsa sull'argomento.)

  2. XSS - Disinfetta i dati sulla strada in ...

    • HTML Purifier è in uso da molto tempo ed è ancora attivamente aggiornato. Puoi usarlo per disinfettare l'input dannoso, pur consentendo una whitelist generosa e configurabile di tag. Funziona alla grande con molti editor WYSIWYG, ma potrebbe essere pesante per alcuni casi d'uso.

    • In altri casi, in cui non vogliamo affatto accettare HTML / Javascript, ho trovato utile questa semplice funzione (e ha superato più audit su XSS):

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS - Disinfetta i dati in uscita ... a meno che tu non garantisca che i dati siano stati adeguatamente disinfettati prima di aggiungerli al tuo database, dovrai disinfettarli prima di mostrarli al tuo utente, possiamo sfruttare queste utili funzioni PHP:

    • Quando si chiama echoo printper visualizzare i valori forniti dall'utente, utilizzare a htmlspecialcharsmeno che i dati non siano stati adeguatamente disinfettati sicuri e sia consentito visualizzare HTML.
    • json_encode è un modo sicuro per fornire valori forniti dall'utente da PHP a Javascript
  4. Chiamate comandi esterni della shell usando exec()o le system()funzioni o backtickall'operatore? In tal caso, oltre a SQL Injection e XSS potresti avere un'ulteriore preoccupazione da affrontare, gli utenti che eseguono comandi dannosi sul tuo server . È necessario utilizzare escapeshellcmdse si desidera sfuggire all'intero comando O escapeshellargper sfuggire ai singoli argomenti.


si potrebbe usare invece mb_encode_numericentity? Dal momento che codifica tutto?
drtechno,

@drtechno - mb_encode_numericentityè discusso nel htmlspecialcharslink su # 3 XSS
webaholik il

5

Il modo più semplice per evitare errori nella sanificazione dell'input e nella fuga di dati consiste nell'utilizzare un framework PHP come Symfony , Nette ecc. O parte di tale framework (motore di template, livello di database, ORM).

Il motore di creazione di modelli come Twig o Latte ha l'output di escape per impostazione predefinita: non è necessario risolverlo manualmente se è stato eseguito l'escaping corretto dell'output a seconda del contesto (parte HTML o Javascript della pagina Web).

Framework sta automaticamente disinfettando l'input e non dovresti usare direttamente le variabili $ _POST, $ _GET o $ _SESSION, ma attraverso meccanismi come routing, gestione delle sessioni ecc.

E per il livello di database (modello) ci sono framework ORM come Doctrine o wrapper attorno a PDO come Nette Database.

Puoi leggere di più qui - Che cos'è un framework software?


3

Volevo solo aggiungere che in tema di escape dell'output, se usi php DOMDocument per creare il tuo output html, questo automaticamente scapperà nel giusto contesto. Un attributo (valore = "") e il testo interno di un <span> non sono uguali. Per essere sicuri contro XSS leggi questo: OWASP XSS Prevention Cheat Sheet


2

Non disinfettare mai l'input.

Disinfetta sempre l'output.

Le trasformazioni applicate ai dati per renderlo sicuro per l'inclusione in un'istruzione SQL sono completamente diverse da quelle applicate per l'inclusione in HTML sono completamente diverse da quelle applicate per l'inclusione in Javascript sono completamente diverse da quelle applicate per l'inclusione in LDIF completamente diversi da quelli applicati all'inclusione nei CSS sono completamente diversi da quelli applicati all'inclusione in un'e-mail ....

Con tutti i mezzi di ingresso validate - decidere se si deve accettare per l'ulteriore elaborazione o dire che l'utente è inaccettabile. Ma non applicare alcuna modifica alla rappresentazione dei dati fino a quando non sta per lasciare la terra PHP.

Molto tempo fa qualcuno ha cercato di inventare un meccanismo a misura unica per sfuggire ai dati e abbiamo finito con " magic_quotes " che non sfuggiva correttamente ai dati per tutte le destinazioni di output e ha portato a installazioni diverse che richiedevano codice diverso per funzionare.


un problema è che non è sempre un attacco al database e tutti gli input dell'utente devono essere protetti dal sistema. non solo un tipo di lingua. Quindi sui tuoi siti, quando enumeri i tuoi dati $ _POST, anche con l'uso del bind, potrebbero scappare abbastanza per eseguire shell o anche altro codice php.
drtechno,

"Non è sempre un attacco al database": "Le trasformazioni applicate ai dati per renderlo sicuro per l'inclusione in un'istruzione SQL sono completamente diverse da quelle ...."
symcbean,

"tutti gli input dell'utente devono essere protetti dal sistema": no, il sistema deve essere protetto dall'input dell'utente.
symcbean,

bene ho finito le parole, ma sì, è necessario impedire che l'input abbia effetto sul funzionamento del sistema. per chiarire questo ...
drtechno,

Sia l'input che l'output devono essere disinfettati.
Tajni,

1

Non fidarti mai dei dati dell'utente.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

La trim()funzione rimuove gli spazi bianchi e altri caratteri predefiniti da entrambi i lati di una stringa.

La stripslashes()funzione rimuove le barre rovesciate

La htmlspecialchars()funzione converte alcuni caratteri predefiniti in entità HTML.

I caratteri predefiniti sono:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;

1
Da cosa proteggerebbe? Questo è per XSS? Perché si chiama clean_inputallora? Perché dovresti voler eliminare le barre?
Dharman,

5
ATTENZIONE: Questo non rende magicamente sicuri i dati dell'utente. Questa funzione danneggerà inutilmente i tuoi dati senza proteggerti da nulla. NON USARLO!
Dharman,

La tua affermazione è falsa.
Erik Thiart,

0

C'è l'estensione del filtro ( howto-link , manuale ), che funziona abbastanza bene con tutte le variabili GPC. Non è una cosa da fare per magia, dovrai comunque usarla.

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.