解析HTML用户输入 [英] Parse HTML user input

查看:98
本文介绍了解析HTML用户输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个来自用户的字符串( $ input )。我可以去,只允许允许使用的标签。我可以使用<$ c转换为文本$ C>用htmlspecialchars()。

 函数html($ input){
$我可以用文本替换所有我不想要的标签。 input ='< bl>'。htmlspecialchars($ input)。'< / bl>'; // bl是我定义的自定义标签(代表块)
global $ open;
$ open = []; //开放标签数组
for($ i = 0; $ i if(!in_array('code',$ open)&& amp ;!in_array('codebl',$ open)){//如果我们解析
$ input = preg_replace_callback('#^(。{'。$ i。'})& lt;(em | i函数($ match){
global $ open; / del | sub | sup | sml | code | kbd | pre | codebl | quote | bl | sbl)& gt; \s *#s' /...然后向数组添加新标签
array_push($ open,$ match [2]);
return $ match [1]。'<'。match [2]。' >'; //并替换它们
},$ input);
$ input = preg_replace_callback('#^(。{'。$ i。'})(https?):\ / \ /([^ \s\(\)<> ;] +)#',函数($ m){
return $ m [1]。'< a href ='。$ m [2]。'://'.$m [3] 。'target =_ blank>'。$ m [3]。'< / a>';
},$ input,-1,$ num); //简单链接
$ i + = $ num * 9;
$ input = preg_replace_callback('#^(。{'。$ i。'})\\\
\\\
#',function($ m){
返回$ m [1]。'< / bl>< bl>';
},$ input); //更多此元素
}
if(end( $ open)){// Close tags
$ input = preg_replace_callback('#^(。{'。$ i。'})& lt; /('。end($ open)。')& ($ match){
global $ open;
array_pop($ open);
return trim($ match [1])。'< /'。 $ match [2]。'>';
},$ input);
}
}
while($打开){//处理未关闭的标记
$ input。='< /'。end($ open)。'>';
array_pop($ open);
}
返回$ input;

$ / code>

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

  function html($ input){
$ input = preg_replace([#&([^ Az])#,#<([^ Az /])#,#& $#,#< $# ],['& $ 1','& lt; $ 1','& amp;','& lt;'],$ input); //修正单个<和&s
$ open = []; //开放标签数组
$ close = false; //当前标签是一个关闭标签吗?
for($ i = 0; $ i <= strlen($ input); $ i ++){//开始循环
if($ tag){//我们在标签中吗?
if(preg_match(/ [^ az] /,$ input [$ i])){//标记已经结束
if($ close){
$ close = false ;
$ sPos = strrpos(substr($ input,0,$ i),'<')+ 2; //标签的开始位置
$ tag = substr($ input,$ sPos,$ i- $ sPos); //标签名称
if(end($ open)== $ tag){
array_pop($ open); //好,这是一个有效的XML关闭
} else {
$ input = substr($ input,0,$ sPos-2)。 '& lt; /'。 $标签。 substr($ input,$ i); //坏!将标签转换为文本(打开标签将在稍后处理)
}
} else {
$ sPos = strrpos(substr($ input,0,$ i),'<')+ 1; //标签的开始位置
$ tag = substr($ input,$ sPos,$ i- $ sPos); //标签名称
if(in_array($ tag,['em','i','del','sub','sup','sml','code','kbd','pre ','codebl','bl','sbl'])){//它是一个可接受的标签吗?
array_push($ open,$ tag); //将它添加到数组
$ j = $ i + 1;
while(preg_match(/ \s /,$ input [$ j])){//摆脱空白
$ j ++;
}
$ input = substr($ input,0,$ sPos - 1)。 '<'。 $标签。 '>'。 substr($ input,$ j); //看起来合法
} else {
$ input = substr($ input,0,$ sPos - 1)。 '& lt;'。 $标签。 substr($ input,$ i); //坏!将标签转换为文本
}
}
$ tag = false;
}
} else if(!in_array('code',$ open)&&!in_array('codebl',$ open)&&!in_array('pre',$ open )){//标准解析文本
if($ input [$ i] =='<'){//是标签吗?
$ tag = true;
if($ input [$ i + 1] =='/'){//它是一个关闭标签吗?
$ i ++;
$ close = true;
}
} else if(substr($ input,$ i,4)=='http'){//链接
if(preg_match('#^。{'。$ i ')(https?):\ / \ /([^ \ s\(\)<>] +)#',$ input,$ m)){
$插入='< 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] ==\\\
&& $输入[$ i + 1] ==\\\
){//插入< bl>标记?(我用它来分隔文本部分)
$ input = substr($ input,0,$ i + 1)。'< / bl>< bl>'substr($ input,$ i + 1);
}
} else {//我们在代码标记
if(substr($ input,$ i + 1,strlen(end($ open))+ 3)=='< /'。current($ open)。'>'){
$ array $ b $ else $($ input $ substring $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' 0,$ i)。 '& lt;'。 substr($ input,$ i + 1);
$ i + = 3; //代码标签有原始文本
} elseif(in_array('code',$ open)&& $ input [$ i] ==\\\
){//内联不允许换行标签,转换为< codebl>
$ open [count($ open) - 1] ='codebl';
$ input = substr($ input,0,strrpos($ input,'< code>))。 '< codebl>'。 ($ input,strrpos($ input,'code>')),' 6)。 '< / codebl>'。 ($ input,strpos($ input,'code>')),'< ;
$ i + = 4;
}
}
}
while($ open){//处理开放标记
$ input。='< /'。end($ open)。 '>';
array_pop($ open);
}
返回'< bl>'。$ input。'< / bl>';
}

我知道这有点多风险,但您可以先假设输入的好,然后过滤出明显发现为不好的东西。


Let's say I have a string from the user ($input). I can go and strip tags, to allow only allowed tags in. I can convert to text with htmlspecialchars(). I can even replace all tags I don't want with text.

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

The problem is that after that, there is no way to write literally &lt;i&lgt;&lt;/i&gt;, because it will be automatically parsed into either <i></i> (if you write <i></i>), or &amplt;i&ampgt;&amplt;/i&ampgt; (if you write &lt;i&gt;&lt;/i&gt;). I want the user to be able to enter &lt; (or any other HTML entity) and get &lt; back. If I just send it straight to the browser unparsed, it would (obviously) be vulnerable to whatever sorcery the hackers are trying (and I'm letting) to (be) put on my site. So, How can I let the user use any of the pre-defined set of HTML tags, while still letting them use html entities?

解决方案

This is what I eventually used:

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

I know it's a bit more risky, but you can first assume the input's good, then filter out the stuff explicitly found as bad.

这篇关于解析HTML用户输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆