5

在一个 Web 项目中,我们使用 PHP 中的 OWASP ESAPI 进行输出编码。在某些时候,我们希望允许 HTML 的子集用于小的格式化选项(例如,<i><b>),同时禁止所有其他标记和特殊字符(因此它们使用&...;语法进行实体编码)。

我看到以下实现这一目标的可能性:

  1. 告诉OWASP ESAPI 编码器将这些标签列入白名单/允许这些标签,以便它只正确编码所有其他 HTML 标签和实体。但这似乎不受支持。但是,我们可以(也许)编写一个允许这样做的补丁。
  2. 使用 ESAPI 编码后解码白名单标签。这可以被攻击吗?
  3. 对此用例使用其他一些输出编码技术。还有其他图书馆吗?

特别是,我需要将以下标签和属性列入白名单:

  • <br>
  • <i>
  • <b>
  • <u>
  • <big>
  • <small>
  • <sub>
  • <sup>
  • <font color="...">
  • <ul>+<li>
  • <ol>+<li>

请注意,我们的应用程序对安全至关重要。这意味着我们要实现的任何方法都应该只接受上面的标签(可能还有更多的仅格式化标签),其他一切都必须正确地进行实体编码。通过查看代码的(简单)代码/解释,毫无疑问应该很容易验证这是真的。代码越短,审查就越容易。完全手工制作的编码器对此并不好。

4

3 回答 3

2

听起来您实际上正在寻找的是 HTMLPurifier

http://htmlpurifier.org/

FWIW 我根本不隶属于 HTMLPurifier,我是 OWASP ESAPI 项目的项目负责人。

于 2013-07-15T20:56:13.843 回答
0

在我过去从事的几个项目中,我使用 Codeigniter 安全类作为实现我自己的安全功能的指南。

在此处查看 GitHub 上的源代码:https ://github.com/EllisLab/CodeIgniter/blob/develop/system/core/Security.php

这段代码中有几个部分与编码和xss有关。可能对您的情况有用。

于 2013-07-07T13:31:01.640 回答
-1

查看下面的代码,在 PHP 5.3 上测试,工作正常。添加了代码注释。

<?php

// test input data
$data = 'br1<br>,br3<br >,      br4<br/>,br2<br />, <i>i1</i><b>b1</b> foo<b onmouseover="alert(hi);">b1</b><u tricky="hello">u1</u> <big>big1</big><small>small1</small> <sub>sub1</sub><div name="aaaa">div1</div> <sup>sup1</sup><font>font1</font> <font       >font2</font><font onmouseover="alert(\'hi\');"  color="red" style="background-color:green;">font3</font><font onmouseover="alert(\'hi\');"  color="red" style="background-color:green;" >font4</font><ul><li>li1</li></ul><ol><li>li2</li></ol>';

// set the allowed tags and their allowed attribs
// case-insensitive
$allowed = array(
"br" => "", // second value is allowed attrs, "" means all attrs allowed for this tag
"i" => "",
"b" => "",
"u" => "",
"big" => "",
"small" => "",
"sub" => "",
"sup" => "",
"font" => "color,style", // comma separated list of allowed attrs, other attrs will be stripped out
"ul" => "",
"ol" => "",
"li" => "",
);

// this will contain output results
$outdata='';
// this func will do the job
fixit();
// print out the results
echo $outdata;

function start_element_handler($parser, $name, $attrs) {
    global $outdata,$allowed;
    // tag allowed, check further
    if( in_array($name,array_keys($allowed)) ) {
        $attrout="";
        if(!empty($attrs)) {
            foreach($attrs as $attr=>$val) {
                // attr allowed, write normal output, else skip the attr/val
                if(empty($allowed[$name]) || in_array($attr,$allowed[$name]))
                    $attrout=$attrout." ".strtolower($attr)."=\"$val\" ";
            }
        }
        $outdata=$outdata."<".strtolower($name)."$attrout>";
    }
    // tag not allowed, htmlentityencode the output
    else {
        $attrout="";
        if(!empty($attrs)) {
            foreach($attrs as $attr=>$val) {
                $attrout=$attrout." ".strtolower($attr)."=\"$val\" ";
            }
        }
        $outdata=$outdata.htmlentities("<".strtolower($name)."$attrout>",ENT_COMPAT,'UTF-8');
    }
}

function end_element_handler($parser, $name) {
    global $outdata,$allowed;
    // void elements have no ending tags, so skip writing to output
    $voids = array("AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "META", "PARAM", "SOURCE", "TRACK", "WBR");
    if(in_array($name,$voids))
        return;
    $nameout = "</".strtolower($name).">";
    // tag allowed, write normal output
    if( in_array($name,array_keys($allowed)) ) {
        $outdata=$outdata.$nameout;
    }
    // tag not allowed, htmlentityencode the output 
    else {
        $outdata=$outdata.htmlentities($nameout,ENT_COMPAT,'UTF-8');
    }
}

function default_handler($parser, $data) {
    global $outdata,$allowed; $outdata=$outdata.htmlentities($data,ENT_COMPAT,'UTF-8');
}

function fixit() {
    global $data, $allowed, $outdata;
    $outdata="";
    // added temp body tags to make xml parser work fine  
    $data="<body>{$data}</body>";
    // change allowed tags and attrs to uppercase for comparisons later
    $allowed=array_change_key_case($allowed,CASE_UPPER);
    foreach($allowed as $tag=>$attrs) $allowed[$tag] = array_filter(explode(",",strtoupper($attrs)));
    // create the parser
    $parser = xml_parser_create('UTF-8');
    // set to uppercase comparisons
    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
    xml_set_element_handler($parser, 'start_element_handler', 'end_element_handler');
    xml_set_default_handler($parser, 'default_handler');
    // parse the data 
    xml_parse($parser, $data, true);
    xml_parser_free($parser);
    // set output in $outdata variable
    $outdata = str_ireplace(array('&lt;body&gt;','&lt;/body&gt;'), "", $outdata); 
}

?>
于 2013-07-05T08:47:08.847 回答