假设我有一个来自用户的字符串($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.'})<(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].'<'.$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.'})</('.end($open).')>#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;
}
问题是,在那之后,没有办法按字面意思写<i&lgt;</i>
,因为它会被自动解析为<i></i>
(如果你写<i></i>
)或{{ 1}}(如果你写&lt;i&gt;&lt;/i&gt;
)。我希望用户能够输入<i></i>
(或任何其他HTML实体)并返回<
。如果我直接将它直接发送到未经分析的浏览器,它(显然)将容易受到黑客正在尝试(并且我正在)放置在我的网站上的任何巫术的攻击。那么,如何让用户使用任何预定义的HTML标签集,同时仍让他们使用html实体?
答案 0 :(得分:0)
这是我最终使用的:
function html($input) {
$input = preg_replace(["#&([^A-z])#","#<([^A-z/])#","#&$#","#<$#"], ['&$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("/[^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) . '</' . $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 = '<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) . '<' . 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>';
}
我知道它风险更大,但你可以先假设输入是好的,然后过滤掉明显被认为是坏的东西。