PHP a le nouveau beau filter_input
qui, par exemple, vous libèrent de l'obligation de trouver "l'ultime regex pour les e-mails" maintenant qu'il existe une fonction intégrée FILTER_VALIDATE_EMAIL
type
Ma propre classe de filtre (qui utilise JavaScript pour mettre en évidence les champs défectueux) peut être lancée par une requête ajax ou un formulaire normal. (voir l'exemple ci-dessous) < ? /** * Pork Formvalidator. valide les champs par regex et peut les sanitiser. Utilise les fonctions intégrées de PHP filter_var et des regex supplémentaires. * @package pork */
/**
* 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;
}
}
Bien sûr, gardez à l'esprit que vous devez aussi faire l'échappement de vos requêtes sql en fonction du type de base de données que vous utilisez (mysql_real_escape_string() est inutile pour un serveur sql par exemple). Vous voudrez probablement gérer cela automatiquement au niveau de votre couche d'application appropriée, comme un ORM. De plus, comme mentionné ci-dessus : pour la sortie en html, utilisez les autres fonctions dédiées de php comme htmlspecialchars ;)
Pour autoriser réellement la saisie HTML avec des classes et/ou des balises dépouillées, utilisez l'un des paquets de validation xss dédiés. N'ÉCRIVEZ PAS VOS PROPRES REGEX POUR ANALYSER LE HTML !
48 votes
De nos jours, pour éviter les injections SQL, utilisez PDO ou MySQLi.
93 votes
Utiliser PDO ou MySQLi ne suffit pas. Si vous construisez vos instructions SQL avec des données non fiables, comme
select * from users where name='$name'
, alors peu importe si vous utilisez PDO, MySQLi ou MySQL. Vous êtes toujours en danger. Vous devez utiliser des requêtes paramétrées ou, si vous le devez, utiliser des mécanismes d'échappement sur vos données, mais cela est beaucoup moins préférable.30 votes
@AndyLester Suggérez-vous que quelqu'un utilise PDO sans instructions préparées? :)
75 votes
Je dis que dire "Utilisez PDO ou MySQLi" n'est pas une information suffisante pour expliquer aux débutants comment les utiliser en toute sécurité. Vous et moi savons que les requêtes préparées sont importantes, mais je ne pars pas du principe que tout le monde qui lit cette question le sait. C'est pourquoi j'ai ajouté des instructions explicites.
0 votes
Alors l'utilisation de PDO::prepare() ou PDO::quote() est la bonne réponse dans ce cas ?
35 votes
Le commentaire d'Andy est tout à fait valide. J'ai récemment converti mon site web mysql en PDO en pensant que j'étais maintenant en quelque sorte à l'abri des attaques par injection. Ce n'est que pendant le processus que j'ai réalisé que certaines de mes déclarations sql étaient encore construites en utilisant des entrées d'utilisateur. J'ai alors corrigé cela en utilisant des instructions préparées. Pour un novice complet, il n'est pas totalement clair qu'il y a une distinction car de nombreux experts font remarquer d'utiliser PDO mais ne précisent pas la nécessité d'utiliser des instructions préparées. L'hypothèse étant que cela est évident. Mais pas pour un novice.
0 votes
Je suppose que cette réponse fonctionne : stackoverflow.com/questions/60174/…
9 votes
@Christian: GhostRider et AndyLester ont raison. Que ceci soit une leçon en communication. J'étais novice une fois et c'était nul car les experts ne savent tout simplement pas comment communiquer.
0 votes
Le commentaire d'Andy est entièrement valide. J'ai récemment converti mon site web mysql en PDO en pensant que j'étais désormais à l'abri des attaques par injection. Ce n'est que pendant le processus que j'ai réalisé que certaines de mes requêtes sql étaient encore construites en utilisant des données utilisateur. J'ai ensuite corrigé cela en utilisant des instructions préparées. Pour un novice complet, il n'est pas totalement clair qu'il y a une distinction car de nombreux experts mentionnent l'utilisation de PDO mais ne spécifient pas de Formulaire .