From 6a48509e7b01e4a4dbab90e665a9df864ea4c059 Mon Sep 17 00:00:00 2001 From: Thomas Ingles Date: Wed, 7 Feb 2024 16:55:32 +0100 Subject: [PATCH] plxUtils : refacto strCheck + nouvelle sanitizeHtml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouvelle logique pour $tags : `true` == interdit ou liste des admis Les guillemts `"` sont convertis et de nouveau authorisées ds CDATA L'option CDATA est protègé par plxUtils::cdataCheck Previent les attaques XSS JS possible avec le dernier param à `true` Corrige : lien Donnais (description de catégorie, Flux RSS, etc...) tien plxMotor->addCommentaire On autorise les balises HTML :


Où l'on supprime tous les scripts
et ne garde que les attributs src, class et href sans javascript.
On supprime aussi style car imaginons un petit malin faire :

pour les textes sans balise, d'où le remplacement de p par div ds theme/defaut/commentaires Idée : un éditeur HTML leger pour les commentaires. --- core/lib/class.plx.admin.php | 38 ++++++------ core/lib/class.plx.motor.php | 4 +- core/lib/class.plx.utils.php | 108 ++++++++++++++++++++++++++++----- themes/defaut/commentaires.php | 2 +- 4 files changed, 115 insertions(+), 37 deletions(-) diff --git a/core/lib/class.plx.admin.php b/core/lib/class.plx.admin.php index 1650d3519..411095b2d 100644 --- a/core/lib/class.plx.admin.php +++ b/core/lib/class.plx.admin.php @@ -284,11 +284,11 @@ public function editConfiguration($plxConfig, $content) { # est inutile : valeur numerique, champs uniquement avec caractères alphanumérique $content = plxUtils::strCheck($v); } elseif(in_array($k, array('description', 'feed_footer'))) { - # On tolère quelques balises par défaut : , , , , , + # On tolère les balises HTML $content = plxUtils::strCheck($v, true); } else { - # Aucune balise HTML tolérée - $content = plxUtils::strCheck($v, true, null); + # Aucune balise HTML tolérée + $content = plxUtils::strCheck($v, true, true); } ?> @@ -971,12 +971,12 @@ public function editCategories($content, $action=false) { - - - - - - + + + + + + plxPlugins->callHook('plxAdminEditCategoriesXml')); @@ -1112,7 +1112,7 @@ public function editStatiques($content, $action=false) { - + @@ -1291,15 +1291,15 @@ public function editArticle($content, &$id) { <?= plxUtils::strCheck(trim($content['title']), true) ?> - ]]> - ]]> - - - - - - - + + + + + + + + + - ]]> + - +

', true) ?>
 	
  $length ? substr($str, 0, $length) . $add_text : $str;
@@ -900,28 +900,37 @@ public static function strCut($str='', $length=25, $type='', $add_text='...') {
 	}
 
 	/**
-	 * Méthode qui retourne une chaine de caractères formatée en fonction du charset
+	 * Méthode qui retourne une chaine de caractères formatée en fonction du charset ou encapsulé avec CDATA
+	 * Possibilité de la nettoyer de toutes ou certaines balises HTML
 	 *
-	 * @param	str		chaine de caractères
-	 * @param	cdata	encapsule str dans  si true et str non nulle
-	 * @param	tags	balises HTML autorisées dans  ou null
-	 * @return	string	chaine de caractères tenant compte du charset
+	 * @param	str		string			Chaine de caractères à formater OU à encapsulé
+	 * @param	cdata	bool			Encapsule str avec  si true
+	 * @param	tags	bool||string	true : aucune Balises HTML || '', false : Toutes autorisées ou Liste* des autorisées '
...' *(array PHP>=7.4) + * @param clear bool true : scripts interdit et attributs nettoyés (nettoyage du html) + Effet de bords : valide et ferme les balises non fermées + * @return string Chaine vide ou chaine encapsulé par CDATA ou Chaine de caractères tenant compte du charset **/ - public static function strCheck($str, $cdata=false, $tags='
') { + public static function strCheck($str, $cdata=false, $tags='', $clear=false) { $str = trim($str); - if ($str === '') { + if(empty($str)) { return ''; } - - if ($cdata) { - # caractère " interdit. Remplacer par " si besoin à la saisie - return ''; + # Important : Appellé avant pour bien effacer toutes les balises soient supprimées avec strip_tags + # et que leurs JS deviennent des noeuds textes impossible a nettoyés car non detectés. + # Elles sont supprimées avant et strip_tags n'aura point à les traiter car inexistantes. + # Si appellé après strip_tags : => JS =>

JS

+ if($clear) { # Scripts et autres attributs interdits : previent les attaques XSS + $str = plxUtils::sanitizeHtml($str); + } + if($tags) { # Balises HTML interdites (true) / Autorisées (liste) + $str = strip_tags($str, is_bool($tags)? '': $tags); + } + if($cdata) { + return ''; } - # ENT_COMPAT : Convertit les guillemets doubles, et ignore les guillemets simples. - # les caractères suivants seont convertis en entités HTML : & > < " - return htmlspecialchars($str, ENT_COMPAT | ENT_HTML5, PLX_CHARSET); + return htmlspecialchars($str, ENT_QUOTES, PLX_CHARSET); } /** @@ -1655,4 +1664,73 @@ public static function sanitizePhpTags(String $content) { public static function sanitizePhp(String $content) { return preg_replace('#\b(fsockopen|proc_open|system|exec|chroot|shell_exec|socket\w*)\b\([^)]*?\)\s*;#', '/* $1() not allowed here */;' . PHP_EOL, $content); } + + + /** + * Nettoie du HTML ET Supprime les balises 'script' (par defaut) et ceux 'inline' pour prévenir des attaques XSS + * @param String|Array $content Texte ou tableau a nettoyer + * @param Array $noNodes Balises a supprimés :defaut: array('script'); :idée+: 'img',style','link','meta' + * @param Array $okAttrs Attributs a gardés :defaut: array('src','href','class'); :idée+: 'style' + * @return String|Array Variable nettoyée + * @author Thomas I. @sudwebdesign & info du web + * @note : Départ inspiré de https://mradeveloper.com/blog/remove-javascript-from-html-with-php (mais oublié '
') + * @todo : No goto? : traversé le DOM par la fin : boucle for inversée + recursive + * @voir : https://www.php.net/manual/fr/domdocument.loadhtml.php#125361 + **/ + public static function sanitizeHtml($content, $noNodes=array('script'), $okAttrs=array('src','href','class')) { + if(empty($content)) return $content; + elseif(is_bool($content)) return $content; + # Initialise les variables + if(is_array($content)) { + $inputArray = $content; + $returnType = 'array'; + } else { + $inputArray = array(trim($content)); + $returnType = 'string'; + } + $sanitizedInput = array(); + $flags = LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR | LIBXML_NOBLANKS | LIBXML_NONET; + # Nettoyage + foreach($inputArray as $input) { + if(!empty($input) and is_string($input)){ + # De https://www.php.net/manual/fr/function.strip-tags.php#119909 + $xml = new DOMDocument('1.0', PLX_CHARSET); + # Suppression des avertissements + libxml_use_internal_errors(true); + # Petit hack pour un bon encodage : voir : https://www.php.net/manual/fr/domdocument.loadhtml.php#95251 + if($xml->loadHTML('' . $input . '', $flags)){ + $xml->encoding = PLX_CHARSET; # Insert propre du charset + RELOAD: # Fix récursif low cost : Voir : https://www.php.net/manual/fr/class.domnamednodemap.php#94078 + foreach($xml->getElementsByTagName('*') as $tag){ # todo : pour faire bien, utiliser une boucle inversée + # Supprime un noeud interdit (script &+) + if(in_array($tag->nodeName, $noNodes)) { + $tag->parentNode->removeChild($tag); + goto RELOAD; # Nvl ordre du DOM, on recommence + } + elseif($tag->attributes->getNamedItem('href') + and stripos($tag->attributes->getNamedItem('href')->value, 'javascript') !== false) { + $tag->removeAttribute('href'); # Suppr inline js + } + + RELOADATTR: # Suppr attributs interdits + foreach($tag->attributes as $attr){ + if(!in_array(strtolower($attr->name), $okAttrs)){ + $tag->removeAttribute($attr->name); + goto RELOADATTR;# Nvl ordre du DOM, on retraverse les attributs + } + } + } + } + # Sauve le HTML nettoyé et bien encodé :voir: https://www.php.net/manual/fr/domdocument.savexml.php#88525 + $input = $xml->saveHTML($xml->documentElement); # sans l'entête xml et le html encapsulant + unset($xml); + } + $sanitizedInput[] = $input; + } + + if($returnType == 'string') { + return $sanitizedInput[0]; + } + return $sanitizedInput; + } } diff --git a/themes/defaut/commentaires.php b/themes/defaut/commentaires.php index e38e183ca..e7cf48669 100644 --- a/themes/defaut/commentaires.php +++ b/themes/defaut/commentaires.php @@ -21,7 +21,7 @@ lang('SAID'); ?> :
-

comContent(); ?>

+
comContent(); ?>