确保一个wysiwyg编辑器的安全是HTMLPurifier真正的唯一途径


Securing a wysiwyg editor - is HTMLPurifier really the only way to go?

我有一个当前为纯文本的注释表单。这很好也很容易,因为我可以使用strip_tag和htmlspecialchars从中剥离出任何类似HTML的东西

然而,有计划把它变成一个HTML表单,支持几个标签<a><b><p><h3>。。。等

我看到的问题是,即使对输入进行strip_tag标记并只保留这些标记,我仍然可以在标记中使用XSS和jscript。

我知道HTML净化器可以用来解决这个问题,但它看起来非常笨重、缓慢,我很难相信没有更好的方法吗?

我曾考虑使用BBCode编辑器,但假设这些代码以BB的形式插入数据库,我将如何将其从BB转换回HTML以显示它?

如果您有时间和精力编写自己的验证方案,请考虑另一个时间,因为这非常复杂。

首先,看看如果不使用递归会发生什么。

恶语:复制此

CopyCopyThisThis

这就剩下了

CopyThis

坏词。。。

BBCode确实是一个非常简单有用的解决方案。

我正在使用MarkItUp!编辑,但你肯定能找到很多其他人。

然后我该如何将它从BB转换回HTML以显示它?

答案很简单:你可以使用preg_replace

我自己实现了这一点,所以我可以让您使用我的解析器代码。它翻译基本标签,以及一些自定义标签,如[center]。添加自己的标签或替换当前的标签非常容易。

该脚本包含一个包含正则表达式和替换项的巨大数组,以及一个preg_replace调用。

function replaceBBcode($str) {
    $replace = array(
        // inline text formats
        '/'[b'](.*?)'['/b']/is'                         => '<b>$1</b>',
        '/'[i'](.*?)'['/i']/is'                         => '<i>$1</i>',
        '/'[u'](.*?)'['/u']/is'                         => '<u>$1</u>',
        '/'[s'](.*?)'['/s']/is'                         => '<s>$1</s>',
        '/'[sup'](.*?)'['/sup']/is'                     => '<sup>$1</sup>',
        '/'[sub'](.*?)'['/sub']/is'                     => '<sub>$1</sub>',
        // headings
        '/'[h1'](.*?)'['/h1']/is'                       => '<h1>$1</h1>',
        '/'[h2'](.*?)'['/h2']/is'                       => '<h2>$1</h2>',
        '/'[h3'](.*?)'['/h3']/is'                       => '<h3>$1</h3>',
        '/'[h4'](.*?)'['/h4']/is'                       => '<h4>$1</h4>',
        '/'[h5'](.*?)'['/h5']/is'                       => '<h5>$1</h5>',
        // formatting tags
        '/'[(?:hr|line)']/is'                           => '<hr />',
        '/'[br'/?']/is'                                 => '<br />',
        // links
        '/'[url=([^']]+)'](.*?)'['/url']/is'            => '<a href="$1">$2</a>',
        '/'[link=([^']]+)'](.*?)'['/link']/is'          => '<a href="$1">$2</a>',
        '/'[url'](.*?)'['/url']/is'                     => '<a href="$1" title="$1">$1</a>',
        '/'[link'](.*?)'['/link']/is'                   => '<a href="$1" title="$1">$1</a>',
        '/'[img=([^']]+)']/is'                          => '<img src="$1" alt="" />',
        // text blocks and block formats
        '/'[font=([^']]+)'](.*?)'['/font']/is'          => '<span style="font-family: $1;">$2</span>',
        '/'[size=([0-9]+)'](.*?)'['/size']/is'          => '<span style="font-size: $1pt;">$2</span>',
        '/'[color=([^']]+)'](.*?)'['/color']/is'        => '<span style="color: $1;">$2</span>',
        '/'[bgcolor=([^']]+)'](.*?)'['/bgcolor']/is'    => '<span style="background-color: $1;">$2</span>',
        '/'[p'](.*?)'['/p']/is'                         => '<p>$1</p>',
        // alignment blocks
        '/'[align=(left|center|right|justify)'](.*?)'['/align']/is'     => '<div style="text-align: $1;">$2</div>',
        '/'[center'](.*?)'['/center']/is'               => '<div style="text-align: center;">$1</div>',
        '/'[left'](.*?)'['/left']/is'                   => '<div style="text-align: left;">$1</div>',
        '/'[right'](.*?)'['/right']/is'                 => '<div style="text-align: right;">$1</div>',
        '/'[justify'](.*?)'['/justify']/is'             => '<div style="text-align: justify;">$1</div>',
        // lists
        '/'[list=(disc|circle|square)'](.*?)'['/list']/is'  => '<ul style="list-style-type:$1;">$2</ul>',
        '/'[list'](.*?)'['/list']/is'                   => '<ul>$1</ul>',
        '/'[list=a'](.*?)'['/list']/s'                  => '<ol style="list-style-type:lower-alpha;">$1</ol>',
        '/'[LIST=a'](.*?)'['/LIST']/s'                  => '<ol style="list-style-type:lower-alpha;">$1</ol>',
        '/'[list=A'](.*?)'['/list']/s'                  => '<ol style="list-style-type:upper-alpha;">$1</ol>',
        '/'[LIST=A'](.*?)'['/LIST']/s'                  => '<ol style="list-style-type:upper-alpha;">$1</ol>',
        '/'[list=1'](.*?)'['/list']/is'                 => '<ol style="list-style-type:decimal;">$1</ol>',
        '/'[list=I'](.*?)'['/list']/is'                 => '<ol style="list-style-type:upper-roman;">$1</ol>',
        '/'['*']/is'                                    => '<li>',
        // videos
        '/'[(?:youtube|video|media|movie){1}'](?:https?':'/'/)?(?:www'.)?(?:youtube'.com'/watch'?v=|youtube'.com'/v'/|youtu'.be'/)?([a-z0-9'-'_]+)'['/(?:youtube|video|media|movie){1}']/is'
                    => '<iframe width="560" height="315" src="http://www.youtube.com/embed/$1?wmode=opaque" frameborder="0" allowfullscreen></iframe>',
    );
    // do the tags
    $str = preg_replace (array_keys($replace), array_values($replace), $str);  
    return $str; 
}

然而,这对于嵌套标签来说并不好用。为了用嵌套标签替换代码,我使用了这样一个循环:

$str = "... text to process ...";
// remove unwanted tags
$str = strip_tags($str);
// make entities of special chars (not quotes) 
$str = htmlentities($str, ENT_NOQUOTES, $encoding = 'UTF-8');
$str_old="";
do {
    $str_old=$str;
    $str=replaceBBcode($str);
} while ($str_old != $str);
// now $str contains the final html tags