【问题标题】:Parse HTML user input解析 HTML 用户输入
【发布时间】:2014-03-14 14:18:34
【问题描述】:

假设我有一个来自用户 ($input) 的字符串。我可以去strip tags,只允许允许的标签进入。我可以使用htmlspecialchars() 转换为文本。我什至可以用文本替换所有我不想要的标签。

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;
}

问题是,在那之后,就没有办法直接写&amp;lt;i&amp;lgt;&amp;lt;/i&amp;gt;,因为它会自动解析成&lt;i&gt;&lt;/i&gt;(如果你写&lt;i&gt;&lt;/i&gt;)或&amp;amplt;i&amp;ampgt;&amp;amplt;/i&amp;ampgt;(如果你写@ 987654331@)。我希望用户能够输入 &amp;lt;(或任何其他 HTML 实体)并返回 &amp;lt;。如果我只是将其直接发送到未解析的浏览器,它将(显然)容易受到黑客正在尝试(并且我让)放置在我的网站上的任何巫术的攻击。那么,我怎样才能让用户使用任何预定义的 HTML 标签集,同时仍然让他们使用 html 实体?

【问题讨论】:

标签: php html validation parsing user-generated-content


【解决方案1】:

这是我最终使用的:

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>';
}

我知道这有点冒险,但你可以先假设输入是好的,然后过滤掉明确发现是坏的东西。

【讨论】:

  • 为什么?大多数情况下,我只是讨厌库。 :P DIE JQUERY!
猜你喜欢
  • 2011-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多