2014-02-15 208 views
1

比方说,我有一个来自用户的字符串($input)。我可以去,strip tags,只允许允许标签。我可以转换为文本htmlspecialchars()。我甚至可以用文本替换我不想要的所有标签。解析HTML用户输入

function html($input) { 
    $input = '<bl>'.htmlspecialchars($input).'</bl>'; // 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.'})&lt;(em|i|del|sub|sup|sml|code|kbd|pre|codebl|quote|bl|sbl)&gt;\s*#s', function($match) { 
       global $open; //...then add new tags to the array 
       array_push($open,$match[2]); 
       return $match[1].'<'.$match[2].'>'; //And replace them 
      }, $input); 
      $input = preg_replace_callback('#^(.{'.$i.'})(https?):\/\/([^\s"\(\)<>]+)#', function($m) { 
       return $m[1].'<a href="'.$m[2].'://'.$m[3].'" target="_blank">'.$m[3].'</a>'; 
      }, $input, -1, $num); //Simple linking 
      $i += $num * 9; 
      $input = preg_replace_callback('#^(.{'.$i.'})\n\n#', function($m) { 
       return $m[1].'</bl><bl>'; 
      }, $input); // More of this bl element 
     } 
     if (end($open)) { //Close tags 
      $input = preg_replace_callback('#^(.{'.$i.'})&lt;/('.end($open).')&gt;#s', function($match) { 
       global $open; 
       array_pop($open); 
       return trim($match[1]).'</'.$match[2].'>'; 
      }, $input); 
     } 
    } 
    while ($open) { //Handle unclosed tags 
     $input .= '</'.end($open).'>'; 
     array_pop($open); 
    } 
    return $input; 
} 

的问题是,在这之后,有没有办法写字面上&lt;i&lgt;&lt;/i&gt;,因为它会自动解析到任何<i></i>(如果你写<i></i>),或&amplt;i&ampgt;&amplt;/i&ampgt;(如果你写&lt;i&gt;&lt;/i&gt;)。我希望用户能够输入&lt;(或任何其他HTML实体)并获得&lt;。如果我只是直接发送给浏览器,那么它显然会受到黑客正在尝试的任何魔术(以及我放)的影响。那么,我该如何让用户使用任何预先定义的HTML标记集,同时让它们使用html实体?

+0

改为使用HTMLPurifier。 striptags是核弹,htmlpurifier可以是手术刀(但也支持核武器)。 –

+0

@MarcB哦......闪亮! – bjb568

+0

看这个链接 [stackoverflow.com] [1] [stackoverflow.com] [2] [1]:http://stackoverflow.com/questions/ 1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 [2]:http://stackoverflow.com/questions/3577641/how-do-you-parse-and -process-html-xml-in-php – Mortzea

回答

0

这是我最终使用:

function html($input) { 
    $input = preg_replace(["#&([^A-z])#","#<([^A-z/])#","#&$#","#<$#"], ['&amp;$1','&lt;$1','&amp;','&lt;'], $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("/[^a-z]/", $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) . '&lt;/' . $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) . '&lt;' . $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 = '<a href="'.$m[1].'://'.$m[2].'" target="_blank">'.$m[2].'</a>'; 
        $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 <bl> tag? (I use this to separate sections of text) 
       $input = substr($input, 0, $i + 1) . '</bl><bl>' . substr($input, $i + 1); 
      } 
     } else { // We're in a code tag 
      if (substr($input, $i+1, strlen(end($open)) + 3) == '</'.current($open).'>') { 
       array_pop($open); 
       $i += 2; 
      } elseif ($input[$i] == '<') { 
       $input = substr($input, 0, $i) . '&lt;' . 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 <codebl> 
       $open[count($open) - 1] = 'codebl'; 
       $input = substr($input, 0, strrpos($input,'<code>')) . '<codebl>' . substr($input, strrpos($input,'<code>') + 6, strpos(substr($input, strrpos($input,'<code>')),'</code>') - 6) . '</codebl>' . substr($input, strpos(substr($input, strrpos($input,'<code>')),'</code>') + strrpos($input,'<code>') + 7); 
       $i += 4; 
      } 
     } 
    } 
    while ($open) { //Handle open tags 
     $input .= '</'.end($open).'>'; 
     array_pop($open); 
    } 
    return '<bl>'.$input.'</bl>'; 
} 

我知道这是更加危险了一点,但你可以先假设输入的好,然后筛选出的东西明确发现的那样糟糕。

+1

为什么?大多数我只是讨厌库。 :P DIE JQUERY! – bjb568