Security

restriction::restrictHtmlTags()メソッド

2008年7月27日

先日取り上げた複数ユーザで使用時のアイテムパースでの脆弱性の回避について、結論が出せた。

やはり、ホワイトリストで対処するのが一番確実なやり方なので、利用可能なタグをホワイトリストで用意し、それに該当しないものはずべてhtmlspecialchars()互換の方法で無効化することにした。

複数ユーザで使用しているときの制限に関するメソッドを集めるためのクラスを一つ作成し、ここに目的のメソッドを配置(restriction::restrictHtmlTags())した。

<?php
class restriction {
    static private $allowHtmlTags=array();
    static public function init(){
        self::$allowHtmlTags['tags']='a|address|area|b|basefont|bgsound|big|blink|blockquote|br|caption|center|cite|code|colgroup'.
            '|dd|del|div|dl|dt|em|fieldset|font|h1|h2|h3|h4|h5|h6|hr|i|img|ins|label|legend|li|map|marquee|nobr|ol|p|pre|q|rb|rp|rt|ruby|s|samp|small'.
            '|span|strike|strong|sub|sup|table|tbody|td|tfoot|th|thead|tr|tt|u|ul|var';
        self::$allowHtmlTags['props']='align|alt|background|balance|bgcolor|border|bordercolor|bordercolordark|bordercolorlight|class|cellpadding|cellspacing'.
            '|color|compact|cite|clear|face|for|frame|gallervimg|height|href|hspace|id|loop|name|noshade|rules|size|span|src|start|summary|target|type|usemap|value|volume|vspace|width|';
        self::$allowHtmlTags['pattern']=array(
            '!&lt;(/?)(<%tags%>)(([\s]+(<%props%>)="(http:|https:|ftp:|[^":])[^":]*")*)([\s]*/?)&gt;!i'=>'<$1$2$3$7>',
            '!&lt;%([\s\S]*?)%&gt;!'=>'<%$1%>',
            '!&lt;--([\s\S]*?)--&gt;!'=>'<!--$1-->'
        );
    }
    static public function allowHtmlTags($tags=0, $props=0, $patterns=0){
        static $search=0,$replace=0;
        // When the paramers are not given, return the cached values
        if ($search && $replace && !($tags||$props||$patterns)) return array($search,$replace);
        // When the paramers are given, change the setting.
        if ($tags) {
            if (is_array($tags)) $tags=implode('|',$tags);
            if (preg_match('/[\|a-z]/i',$tags)) self::$allowHtmlTags['tags'].='|'.$tags;
        }
        if ($props) {
            if (is_array($pros)) $pros=implode('|',$pros);
            if (preg_match('/[\|a-z]/i',$pros)) self::$allowHtmlTags['pros'].='|'.$pros;
        }
        if ($patterns) {
            foreach($patterns as $key=>$value) self::$allowHtmlTags['pattern'][$key]=$value;
        }
        $search=$replace=array();
        foreach(self::$allowHtmlTags['pattern'] as $key=>$value){
            $search[]=str_replace(
                array('<%tags%>','<%props%>'),
                array(self::$allowHtmlTags['tags'],self::$allowHtmlTags['props']),
                $key);
            $replace[]=$value;
        }
        // Return the values, finally.
        return array($search,$replace);
    }
    static public function restrictHtmlTags($html){
        list($search,$replace)=self::allowHtmlTags();
        $html=str_replace(array('&','<','>'),array('&amp;','&lt;','&gt;'),$html);
        $html=preg_replace($search,$replace,$html);
        $html=str_replace('&amp;','&',$html);
        return $html;
    }
    
}

scriptタグなどは当然ながら、含まれていない。また、hrefやsrc, width, heightなど、タグにおけるプロパティ指定についても、ホワイトリストで指定することにした。当然ながら、onclickやonmouseoverなどのスクリプト関連は入っていない。また、""で囲まれた値指定の部分で『:』を利用する際は、『http:』『https:』『ftp:』の3つだけが使えるようにした。これは、『javascript:』などの記述が使えないようにするため。

同じやり方は、Nucleusにも使えるはず。superadminでないユーザの書いた記事はすべてこのメソッドを通せばよい。また、superadminでないユーザのPOST値すべてについてこのメソッドで処理すると、blog-adminがブログの設定値(短縮名・説明・URLなど)にスクリプトを仕込んだり出来なくなるはず(安全なタグは記述できる)。

なお、<div style="text-align: center">などのスタイルシート直接指定は、デフォルトでは禁止されている。これは、IEではスタイルシートでスクリプトが使えるので禁止しないといけないため。ただし、タグでのクラス指定は出来るので、別途用意したCSSでクラスごとのスタイルを指定しておけば、タグでそれらのスタイルを呼び出すことは可能。

NP_0NoScriptByUser???

コメント

Kat (2008年7月29日 17:14:57)

このメソッドは、管理者以外の$_POST値にも適用することにした。ただし、キー名がbinayで終わる時は適用されない。

コメント送信