Qual è il metodo migliore per sanitizzare l'input dell'utente con PHP?

Esiste una function catchall da qualche parte che funzioni bene per sanare l'input dell'utente per l'iniezione SQL e gli attacchi XSS, pur consentendo determinati tipi di tag html?

È un comune malinteso che l'input dell'utente possa essere filtrato. PHP ha anche una "funzionalità" (ora deprecata), chiamata magic-quotes, che si basa su questa idea. Non ha senso. Dimentica il filtraggio (o la pulizia, o qualsiasi altra cosa la chiami).

Quello che dovresti fare, per evitare problemi è abbastanza semplice: each volta che incorpori una string all'interno di un codice estraneo, devi scapparla, secondo le regole di quella lingua. Ad esempio, se si incorpora una string in un targeting SQL MySql, è necessario sfuggire alla string con la function MySql per questo scopo ( mysqli_real_escape_string ).

Un altro esempio è HTML: se incorpori stringhe all'interno del markup HTML, devi sfuggire a htmlspecialchars . Ciò significa che each singola echo o istruzione di print deve utilizzare htmlspecialchars .

Un terzo esempio potrebbe essere i comandi di shell: se intendi incorporare stringhe (come argomenti) a comandi esterni e chiamali con exec , devi usare escapeshellcmd e escapeshellarg .

E così via e così via …

L' unico caso in cui è necessario filtrare triggersmente i dati, è se si accettano input preformattati. Per esempio. se lasci che i tuoi utenti pubblichino markup HTML, che intendi visualizzare sul sito. Tuttavia, dovresti essere prudente per evitarlo a tutti i costi, poiché non import quanto bene lo filtri, sarà sempre un potenziale buco di sicurezza.

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

Invece, non consentire l'uso dei dati nella creazione del codice SQL . Utilizzare le istruzioni preparate (ovvero utilizzando i parametri in una query model) che utilizza variables associate. È l'unico modo per essere garantito contro l'iniezione SQL.

Si prega di consultare il mio sito web http://bobby-tables.com/ per ulteriori informazioni sulla prevenzione dell'iniezione SQL.

No. Non puoi filtrare genericamente i dati senza alcun context per quello che serve. A volte si vorrebbe prendere una query SQL come input e talvolta si vorrebbe prendere l'HTML come input.

Devi filtrare l'input su una list bianca – assicurati che i dati corrispondano ad alcune specifiche di ciò che ti aspetti. Quindi devi scappare prima di usarlo, a seconda del context in cui lo stai usando.

Il process di escape dei dati per SQL – per prevenire l'iniezione SQL – è molto diverso dal process di escape dei dati per (X) HTML, per prevenire XSS.

PHP ha ora le nuove funzioni filter_input, che per esempio ti liberano dal trovare "l'espressione regolare della posta elettronica finale" ora che esiste un tipo FILTER_VALIDATE_EMAIL predefinito

La mia class di filter (utilizza javascript per evidenziare i campi difettosi) può essere avviata da una richiesta Ajax o da un module normale. (vedi l'esempio sotto)

 /** * Pork.FormValidator * Validates arrayss or properties by setting up simple arrayss. * Note that some of the regexes are for dutch input! * Example: * * $validations = arrays('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date'); * $required = arrays('name', 'email', 'alias', 'pwd'); * $sanatize = arrays('alias'); * * $validator = new FormValidator($validations, $required, $sanatize); * * if($validator->validate($_POST)) * { * $_POST = $validator->sanatize($_POST); * // now do your saving, $_POST has been sanatized. * die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>"); * } * else * { * die($validator->getScript()); * } * * To validate just one element: * $validated = new FormValidator()->validate('[email protected]', 'email'); * * To sanatize just one element: * $sanatized = new FormValidator()->sanatize('<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=arrays(), $mandatories = arrays(), $sanatations = arrays()) { $this->validations = $validations; $this->sanatations = $sanatations; $this->mandatories = $mandatories; $this->errors = arrays(); $this->corrects = arrays(); } /** * Validates an arrays 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 || arrays_search($key, $this->validations) === false) && arrays_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 classs that are. */ public function getScript() { if(!empty($this->errors)) { $errors = arrays(); foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; } $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; $output .= "new FormValidator().showMessage();"; } if(!empty($this->corrects)) { $corrects = arrays(); foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; } $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");'; } $output = "<script type='text/javascript'>{$output} </script>"; return($output); } /** * * Sanatizes an arrays of items according to the $this->sanatations * sanatations will be standard of type string, but can also be specified. * For ease of use, this syntax is accepted: * $sanatations = arrays('fieldname', 'otherfieldname'=>'float'); */ public function sanatize($items) { foreach($items as $key=>$val) { if(arrays_search($key, $this->sanatations) === false && !arrays_key_exists($key, $this->sanatations)) continue; $items[$key] = self::sanatizeItem($val, $this->validations[$key]); } return($items); } /** * * Adds an error to the errors arrays. */ private function addError($field, $type='string') { $this->errors[$field] = $type; } /** * * Sanatize a single var according to $type. * Allows for static calling to allow simple sanatization */ public static function sanatizeItem($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(arrays_key_exists($type, self::$regexes)) { $returnval = filter_var($var, FILTER_VALIDATE_REGEXP, arrays("options"=> arrays("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 devi eseguire l'escape della query sql anche in base al tipo di db che stai utilizzando (mysql_real_escape_string () è inutile per un server sql, ad esempio). Probabilmente vorrai gestirlo automaticamente al tuo livello di applicazione appropriato come un ORM. Inoltre, come detto sopra: per l'output in html usa le altre funzioni dedicate di php come htmlspecialchars;)

Per consentire davvero l'input HTML con classi e / o tag come stripped dipendono da uno dei pacchetti di validation xss dedicati. NON SCRIVERE IL PROPRIO REGEXES A PARSE HTML!

No non c'è.

Innanzitutto, SQL injection è un problema di filtraggio degli input e XSS è un output che ne sfugge uno, quindi non eseguiresti nemless queste due operazioni contemporaneamente nel ciclo di vita del codice.

Regole di base di base

  • Per la query SQL, associare i parametri (come con PDO) o utilizzare una function di escape nativa del driver per le variables di query (come mysql_real_escape_string() )
  • Utilizza strip_tags() per filtrare l'HTML indesiderato
  • Sfuggi a tutti gli altri output con htmlspecialchars() e sii consapevole dei parametri 2nd e 3rd qui.

Per risolvere il problema dell'XSS, dai un'occhiata al purificatore HTML . È abbastanza configurabile e ha una buona reputazione.

Per quanto riguarda gli attacchi SQL injection, assicurati di controllare l'input dell'utente, quindi eseguirlo tramite mysql_real_escape_string (). Tuttavia, la function non sconfiggerà tutti gli attacchi di iniezione, quindi è importnte controllare i dati prima di scaricarli nella string di query.

Una soluzione migliore è usare istruzioni preparate. La libreria PDO e l'estensione mysqli supportno questi.

PHP 5.2 ha introdotto la function filter_var .

Support una grande quantità di filtri SANITIZE, VALIDATE.

http://php.net/manual/en/function.filter-var.php

Un trucco che può aiutare nella circostanza specifica in cui si ha una pagina come /mypage?id=53 e si utilizza l'id in una clausola WHERE è quello di assicurarsi che id sia sicuramente un numero integer, 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 esclude solo un attacco specifico, quindi leggi tutte le altre risposte. (E sì, so che il codice sopra non è eccezionale, ma mostra la difesa specifica).

Quello che stai descrivendo qui è due questioni separate:

  1. Sanificazione / filtraggio dei dati di input dell'utente.
  2. Uscita di fuga.

1) Si dovrebbe sempre presumere che l'input dell'utente sia cattivo.

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

2) Questo è un argomento ampio e dipende dal context dei dati in output. Per HTML ci sono soluzioni come htmlpurifier là fuori. come regola generale, sfuggi sempre a tutto ciò che emetti.

Entrambe le questioni sono troppo grandi per essere inserite in un singolo post, ma ci sono molti post che vanno più nel dettaglio:

Metodi di output PHP

Uscita PHP più sicura

Se stai usando PostgreSQL, l'input da PHP può essere sfuggito 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 string per interrogare il database. Restituisce una string con escape nel formato PostgreSQL senza virgolette.

Volevo solo aggiungere che, in materia di escape dell'output, se si usa php DOMDocument per rendere l'output html esso verrà automaticamente scappato nel giusto context. Un attributo (value = "") e il text interno di un <span> non sono uguali. Per sicurezza contro XSS leggi questo: OWASP XSS Preventson Cheat Sheet

Il modo più semplice per evitare errori nella disinfezione dell'input e l'escaping dei dati è l'utilizzo di framework PHP come Symphony , Nette ecc. O parte di tale framework (motore di template, livello di database, ORM).

Il motore di template come Twig o Latte ha un'output di escape per impostazione predefinita – non è necessario risolverlo manualmente se si è correttamente sfuggiti all'output a seconda del context (parte HTML o Javascript della pagina Web).

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

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

Puoi leggere di più a riguardo qui – Cos'è un framework software?

C'è l'estensione del filter ( howto-link , manual ), che funziona molto bene con tutte le variables GPC. Tuttavia, non è una cosa magica, dovrai comunque usarla.

Il miglior metodo BASIC per sanitizzare l'input dell'utente con PHP:

 function sanitizeString($var) { $var = stripslashes($var); $var = strip_tags($var); $var = htmlentities($var); return $var; } function sanitizeMySQL($connection, $var) { $var = $connection->real_escape_string($var); $var = sanitizeString($var); return $var; }