Fonction de nettoyage de variables avec une installation de php basique

Soyez le premier à donner votre avis sur cette source.

Snippet vu 4 305 fois - Téléchargée 17 fois

Contenu du snippet

Bonjour à tous,

Ayant longuement cherché sur le web, je n'ai pas trouvé de solution de nettoyage des variables simple d'emploi en PHP.

Bien sûr il y a des solutions, mais elles nécéssitent toutes l'installation de certaines extensions de PHP qui sont rarement disponibles en hébergement mutualisé.

J'ai donc créé cette fonction "checkVar()" avec les moyens du bord, c'est-à-dire une installation PHP de base.

Cette fonction est EXPERIMENTALE et évidemment totalement libre de tous droits.

Je ne suis pas un cador en programmation et encore moins en sécurité, c'est pourquoi je la soumet ici à vos critiques aussi bien sur le fond (utilité, pertinence) que sur la forme (réalisation probablement imparfaite) car peut-être ensemble arriverons-nous à en faire quelque chose de fiable.

-

Mini-documentation:

$target: "html" si vous souhaitez afficher le résultat sur une page, ou "sql" si vous souhaitez le concaténer dans une commande SQL

$untrusted_value: valeur saisie par l'utilisateur à vérifier, typiquement en provenance d'un $_POST ou d'un $_GET.

$awaited_type: type de valeur attendu. Valeurs possible: "boolean", "int", "float", "hex", "string", "date", "hour", "url", "email".

$min, $max: limites mini et maxi. Si le type de valeur attendu est numérique (int, float, hex), il s'agit de sa valeur intrinsèque. Si le type de valeur attendu est chaîne se caractère (string, date, url, email), la limite s'applique à la longueur de la chaîne.

$default_value: Valeur renvoyée par défaut par la fonction si le test échoue.

$label: Désignation de la valeur analysée. Utile dans les messages d'erreur.

$array_return: 1 = retourner un rapport d'analyse sous forme de tableau, 0 = retourner simplement la valeur après analyse.

Description du tableau de rapport d'analyse
"ok" => 1 | 0 (indique si le test a réussi ou échoué)
"value" => retour de valeur après analyse ($default_value si le test a échoué)
"error" => Description de l'erreur si le test a échoué.

$die_on_fail: si l'analyse a échoué, 0 = retourner la valeur par défaut, 1 = stopper le script (à utiliser dans les cas critiques où si la valeur est hors limite il s'agit obligatoirement d'une tentative malveillante).

Source / Exemple :


function checkVar($target, $untrusted_value, $awaited_type, $min, $max, $default_value, $label, $array_return, $die_on_fail) {

	$value_accepted = true; $error = ""; $date_format = "d/m/Y"; // $date_format is only for date type validation

	// 1. filters value according to target (web page or database)
	// converts to correct charset, removes unwanted values, encodes special chars
	// does nothing if not $target = ""
	$untrusted_value = filterValue($target, $untrusted_value);

	// 2. checks var content against awaited type
	if($awaited_type != "") {
		$value_accepted = validateType($untrusted_value, $awaited_type, $target, $date_format); // $date_format is only for date type validation
		if(!$value_accepted) { $error .= "bad type, " . $awaited_type . " awaited."; }
	} else {
		// sets var type if not specified, for next check against bounds
		if(is_numeric($untrusted_value)) { $awaited_type = "float"; } else { $awaited_type = "string"; }
	}

	// 3. checks var content against bounds
	if($value_accepted) {

		// numeric : checks var content against values bounds
		if($awaited_type == "int" || $awaited_type == "float" || $awaited_type == "hex") {
			$value_accepted = validateValue($untrusted_value, $min, $max);
			if(!$value_accepted) { $error .= "bad value, " . $min . " to " . $max . " accepted."; }
		}

		// string : checks var content against length bounds
		if($awaited_type == "string" || $awaited_type == "date" || $awaited_type == "url" || $awaited_type == "email") {
			$value_accepted = validateLength($untrusted_value, $min, $max);
			if(!$value_accepted) { $error .= "bad length, " . $min . " to " . $max . " chars awaited."; }
			}
		}

		if($value_accepted) {

			switch($array_return) {

			case 0: // returns a single value without feedback
			return $untrusted_value; break;

			case 1: // returns an array with filtered value or default value with error feedback if validation fails (useful for form validation)
			return array("ok"=>true, "value"=>$untrusted_value, "error"=>"");
		}

	} else {

		if($die_on_fail) {
			exit("Fatal error :: bad var value detected" ,1);
			if($debug_mode == "on") { echo "<br>'" . $label . "' " . $error; }
		}

		switch($array_return) {

			case 0: // returns a single value without feedback
			return $default_value; break;

			case 1: // returns an array with filtered value or default value with error feedback if validation fails (useful for form validation)
			return array("ok"=>false, "value"=>$default_value, "error"=>"'" . $label . "' " . $error);
		}
	}
}

function filterValue($target, $value) {

	switch($target) {
		case "": return $value; break;
		case "sql": $db_connection = mysql_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD); return mysql_real_escape_string($value, $db_connection); break; // mysql connexion is mandatory for mysql_real_escape_string use
		case "html": return htmlentities($value, ENT_QUOTES, "UTF-8"); // change UTF-8 if you're using another page code
	}
}

function validateType($value, $type, $target, $date_format) {

	switch($type) {
		case "boolean": return is_bool($value); break;
		case "int": return ctype_digit($value); break;
		case "float": return is_float($value); break;
		case "hex": return ctype_xdigit($value); break;
		case "string": return validateString($target, $value); break;
		case "date": return dateFormat($value, $date_format, ""); break;
		case "hour": return validateHour($value); break;
		case "url": return validateUrl($value); break;
		case "email": return validateEmail($value); break;
		default: return false;
	}
}

function validateString($target, $string) { // [] () / . ? ! _ - &aa[aaaa]; &000;

	if($string == "") { return true; }

	switch($target) {
		case "html": $pattern = "#^([A-Za-z0-9\.\?\!\[\]\(\)\s'/_-]|&[a-z]{2,6};|&\#[0-9]{3,3};){1,}$#"; break;
		case "sql": $pattern = "#^([ÉÈÊËÜÛÎÔÄÏÖÄÅÇA-Zéèëêüûçîôâïöäåaa-z0-9\.\?\!\[\]\(\)\s'/_-]|&[a-z]{2,6};|&\#[0-9]{3,3};){1,}$#"; break;
		case "": $pattern = "#^([A-Za-z0-9\.\?\!\[\]\(\)\s'/_-]){1,}$#"; break;
		default: return false;
	}
	
	return preg_match($pattern, $string) ? $string : false;
}

function validateHour($hour) {
	$pattern = "#^[0-2]{1,1}[0-9]{1,1}:[0-2]{1,1}[0-9]{1,1}$#";
	return preg_match($pattern, $hour) ? $hour : false;
}

function validateUrl($url) {
	$pattern = "#^(http://)?((www|3w|w3)\.)?[A-Za-z0-9_-]+(\.[A-Za-z]{2,4})+([^/\.]/[A-Za-z0-9_-]{0,})*(\.[A-Za-z0-9]{1,})?$#";
	return preg_match($pattern, $url) ? $url : false;
}

function validateEmail($email) {
	$pattern = "#^[A-Za-z0-9._-]+@[a-z0-9._-]{2,}\.[A-Za-z]{2,4}$#";
	return preg_match($pattern, $email) ? $email : false;
}

function validateValue($value, $min, $max) {
	return (($min == "" || $value >= $min) && ($max == "" || $value <= $max));
}

function validateLength($value, $min, $max) {
	return (($min == "" || strlen($value) >= $min) && ($max == "" || strlen($value) <= $max));
}

function dateFormat($d_date, $s_input_format, $s_output_format) {

	// checks if $d_date sticks to $s_input_format
	// checks if $d_date is a valid date
	// returns $d_date converted to $s_output_format or true if $s_output_format is an empty string

	$return = false;

	// parse date format
	$a_year_formats = array("Y","y");
	$a_month_formats = array("m","M","n");
	$a_day_formats = array("d","j");
	$a_separator_formats = array("/",".","-");

	$a_format = array();

	for($n=0;$n<=strlen($s_input_format)-1;$n++) {

		$letter = substr($s_input_format, $n, 1);

		if(in_array($letter, $a_day_formats)) {
			$a_format["day"] = $letter;
		} elseif(in_array($letter, $a_month_formats)) {
			$a_format["month"] = $letter;
		} elseif(in_array($letter, $a_year_formats)) {
			$a_format["year"] = $letter;
		} elseif(in_array($letter, $a_separator_formats)) {
			$a_format["separator"] = $letter;
		}
	}

	if(count($a_format) == 4) { // year, month, day and separator found

		$year = ""; $month = ""; $day = "";

		// explodes date according to parsed format
		$a_date = explode($a_format["separator"], $d_date);

		foreach($a_date as $i_index=>$s_value) {
			$$a_format[$i_index]["date_part"] = $s_value; // $day = 30 | $month = 12 | $year = 2009
		}

		if(checkdate($month, $day, $year)) {

			if($s_output_format == "") {
				$return = true;
			} else {
				$return = date($s_output_format, strtotime($year . "-" . $month . "-" . $day));
			}
		}
	}

	return $return;
}

Conclusion :


J'attends vos commentaires, venez nombreux !

A voir également

Ajouter un commentaire

Commentaires

malalam
Messages postés
10843
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Hello,

je n'ai pas trop le temps de mater ton code, mais jette quand même un oeil à ça pour du "nettoyage" de variables en standard dans php :
http://fr.php.net/manual/en/book.filter.php
cs_jroger
Messages postés
25
Date d'inscription
mercredi 30 janvier 2002
Statut
Membre
Dernière intervention
26 mai 2009
-
Oui je connais les filtres build-in de PHP. Ils ont le mérite d'unifier ce genre de nettoyage, mais:

- Il faut avoir un hébergeur qui supporte PHP 5.2.0+
- Ils ne gèrent pas le bornage des valeurs numériques et des longueurs de chaines, donc elles ne protègent propablement pas des attaques par cast ou mauvais caractères unicodes (spécifique mysql), ce qui oblige à des contrôles supplémentaires. Bonjour l'usine à gaz.
malalam
Messages postés
10843
Date d'inscription
lundi 24 février 2003
Statut
Modérateur
Dernière intervention
2 mars 2010
17 -
Certes, il faut php 5.2 au moins, mais ça devient très fréquent quand même.
Par contre, VALIDATE_FILTER_INT gère le bornage des valeurs numériques, pour les entiers du moins.
Et FILTER_VALIDATE_REGEXP ou FILTER_CALLBACK permettent au moins de gérer ce qui manquerait éventuellement.
Il manquera toujours quelque chose à un code de validation de données : j'avais fait un code similaire au tien il y a pas mal de temps, en objet celui-là, pour la raison que tu évoques en premier : php5.2 n'était alors pas franchement répandu, et j'avais justement ajouté ces 2 "filtres" très utiles.
Cela n'enlève rien à l'utilité de ton code si on n'a pas php5.2, si ce n'est que j'ajouterais cette possibilité : on ne peut pas imaginer tous les besoins d'un utilisateur. Alors autant lui laisser l'opportunité de répondre lui-même à ces besoins, en lui facilitant la tâche.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.