Analizzare l’input dell’utente HTML

Diciamo che ho una stringa da parte dell’utente ( $input ). Posso andare e rimuovere i tag , per consentire solo i tag consentiti. Posso convertirli in testo con htmlspecialchars() . Posso anche sostituire tutti i tag che non voglio con il testo.

 function html($input) { $input = ''.htmlspecialchars($input).''; // bl is a custom tag that I style (stands for block) global $open; $open = []; //Array of open tags for ($i = 0; $i < strlen($input); $i++) { if (!in_array('code', $open) && !in_array('codebl', $open)) { //If we are parsing $input = preg_replace_callback('#^(.{'.$i.'})<(em|i|del|sub|sup|sml|code|kbd|pre|codebl|quote|bl|sbl)>\s*#s', function($match) { global $open; //...then add new tags to the array array_push($open,$match[2]); return $match[1].''; //And replace them }, $input); $input = preg_replace_callback('#^(.{'.$i.'})(https?):\/\/([^\s"\(\)]+)#', function($m) { return $m[1].''.$m[3].''; }, $input, -1, $num); //Simple linking $i += $num * 9; $input = preg_replace_callback('#^(.{'.$i.'})\n\n#', function($m) { return $m[1].''; }, $input); // More of this bl element } if (end($open)) { //Close tags $input = preg_replace_callback('#^(.{'.$i.'})</('.end($open).')>#s', function($match) { global $open; array_pop($open); return trim($match[1]).''; }, $input); } } while ($open) { //Handle unclosed tags $input .= ''; array_pop($open); } return $input; } 

Il problema è che dopo questo, non c’è modo di scrivere letteralmente <i&lgt;</i> , perché verrà automaticamente analizzato in (se scrivi ), o &amplt;i&ampgt;&amplt;/i&ampgt; (se scrivi <i></i> ). Voglio che l’utente sia in grado di entrare < (o qualsiasi altra entity framework HTML) e ottieni < indietro. Se lo mando semplicemente al browser non analizzato, sarebbe (ovviamente) vulnerabile a qualsiasi stregoneria che gli hacker stanno provando (e sto lasciando) a (essere) messo sul mio sito. Quindi, come posso consentire all’utente di utilizzare uno qualsiasi dei set predefiniti di tag HTML, lasciando comunque che utilizzino quadro html?

Questo è quello che alla fine ho usato:

 function html($input) { $input = preg_replace(["#&([^Az])#","#<([^Az/])#","#&$#","#<$#"], ['&$1','<$1','&','<'], $input); //Fix single "<"s and "&"s $open = []; //Array of open tags $close = false; //Is the current tag a close tag? for ($i = 0; $i <= strlen($input); $i++) { //Start the loop if ($tag) { //Are we in a tag? if (preg_match("/[^az]/", $input[$i])) { //The tag has ended if ($close) { $close = false; $sPos = strrpos(substr($input,0,$i), '<') + 2; //start position of tag $tag = substr($input,$sPos,$i-$sPos); //tag name if (end($open) == $tag) { array_pop($open); //Good, it's a valid XML closing } else { $input = substr($input, 0, $sPos-2) . '</' . $tag . substr($input, $i); //BAD! Convert tag to text (open tag will be handled later) } } else { $sPos = strrpos(substr($input,0,$i), '<') + 1; //start position of tag $tag = substr($input,$sPos,$i-$sPos); //tag name if (in_array($tag, ['em','i','del','sub','sup','sml','code','kbd','pre','codebl','bl','sbl'])) { //Is it an acceptable tag? array_push($open, $tag); //Add it to the array $j = $i + 1; while (preg_match("/\s/", $input[$j])) { //Get rid of whitespace $j++; } $input = substr($input, 0, $sPos - 1) . '<' . $tag . '>' . substr($input, $j); //Seems legit } else { $input = substr($input, 0, $sPos - 1) . '<' . $tag . substr($input, $i); //BAD! Convert tag to text } } $tag = false; } } else if (!in_array('code', $open) && !in_array('codebl', $open) && !in_array('pre', $open)) { //Standard parsing of text if ($input[$i] == '<') { //Is it a tag? $tag = true; if ($input[$i+1] == '/') { //Is it a close tag? $i++; $close = true; } } else if (substr($input, $i, 4) == 'http') { //Link if (preg_match('#^.{'.$i.'}(https?):\/\/([^\s"\(\)<>]+)#', $input, $m)) { $insert = ''.$m[2].''; $input = substr($input, 0, $i) . $insert . substr($input, $i + strlen($m[1].'://'.$m[2])); $i += strlen($insert); } } else if ($input[$i] == "\n" && $input[$i+1] == "\n") { //Insert  tag? (I use this to separate sections of text) $input = substr($input, 0, $i + 1) . '' . substr($input, $i + 1); } } else { // We're in a code tag if (substr($input, $i+1, strlen(end($open)) + 3) == '') { array_pop($open); $i += 2; } elseif ($input[$i] == '<') { $input = substr($input, 0, $i) . '<' . substr($input, $i + 1); $i += 3; //Code tags have raw text } elseif (in_array('code', $open) && $input[$i] == "\n") { //No linebreaks are allowed in inline tags, convert to  $open[count($open) - 1] = 'codebl'; $input = substr($input, 0, strrpos($input,'')) . '' . substr($input, strrpos($input,'') + 6, strpos(substr($input, strrpos($input,'')),'') - 6) . '' . substr($input, strpos(substr($input, strrpos($input,'')),'') + strrpos($input,'') + 7); $i += 4; } } } while ($open) { //Handle open tags $input .= ''; array_pop($open); } return ''.$input.''; } 

So che è un po 'più rischioso, ma puoi prima supporre che l'input sia buono, quindi filtrare le cose che sono state trovate esplicitamente come cattive.