2013-07-17 24 views
5

信頼できないユーザーからのHTMLを受け入れ、サニタイズして自分のウェブサイトのページに安全に含めることができるようにします。これは、マークアップを取り除いたりエスケープしたりするのではなく、<script>やなどの危険なタグ、onloadなどの危険な属性、または背景URLなどの危険なCSSプロパティが含まれていない限り、本質的に変更されていないことを意味します。 (明らかに、一部の古いIEはCSSでjavascript URLを実行しますか?)信頼できないHTML5のサニタイズ

iframeで囲まれた別のドメインからのコンテンツの配信は、iframeの高さをそれは常にいくつかのページのために醜い見えるようになる。

HTML Purifierを調べましたが、まだHTML5をサポートしていないようです。私もGoogle Cajaを調べましたが、私はスクリプトを使用しないソリューションを探しています。

これを達成するライブラリを知っている人はいますか? PHPが好まれますが、乞食はチョーザーになれません。

+0

[raxan data sanitizer](https://searchcode.com/codesearch/view/2955473/) – Gunaseelan

+2

ツール、ライブラリ、またはお気に入りのオフサイトリソースをおすすめするスタックオーバーフローは、有害な回答や迷惑メールを引き付ける傾向があるためです。代わりに、問題を説明し、それを解決するためにこれまでに何が行われているかを記述します。 –

答えて

2

あなたはの線に沿って何かを行うことができるかもしれない:再度

preg_replace('/<\s*iframe\s+[^>]*>.*<\s*\/\s*iframe\s+[^>]*>/i', '', $html); 
preg_replace('/<\s*script\s+[^>]*>.*<\s*\/\s*script\s+[^>]*>/i', '', $html); 
preg_replace('/\s+onload\s+=\s+"[^"]+"/i', '', $html); 

...しかし:あなたは今、あなたは二つの問題があり、正規表現を持っている - これは望んでいた以上に除去することがありますし、より多くを残します欲しかった。

しかし、HTML Purifierはおそらく最も現代的で適切な(そしてオープンソースの)プロジェクトなので、まだそれを使用して、本当に必要な場合は調整する必要があります。

あなたにも、次のいずれかのチェックアウトすることができます:

  • kses - さらに発展KSEの
  • PHP Input Filter - - デファクトスタンダードは、ワードプレスへの道だけでなく
  • htmLawedを発見したフィルタリングすることができますタグと属性

あなた自身のページレイアウトでは、クローズドタグではないからです。

2

別のアプローチをとる方がよいでしょうか? 彼らは何を使うことができるのですか?

この場合は、strip_tagsを使用できます。それはより簡単で、このように多くの制御が可能になります。非常に簡単に将来も拡張する

+1

>この関数はallowable_tagsを使用して許可するタグの属性を変更しません。他のユーザーに表示されるテキストを投稿するときに悪意のあるユーザーが悪用する可能性があるstyleおよびonmouseover属性も含まれます。 – Brian

6

ブラックリストのアプローチは、あなたにアップグレードプレッシャーをかけます。ブラウザーが新しい基準をサポートするたびに、あなたは同じレベルに消毒ツールを描きます。そのような変化は、あなたが思うよりも頻繁に起こります。

ホワイトリスト(明確に定義された例外を含むstrip_tagsによって達成されます)は、ユーザーのオプションを縮小しますが、保存サイトに配置されます。

私自身のサイトでは、非常に信頼できるユーザー(管理者など)のページにブラックリストを適用し、他のすべてのページでホワイトリストを適用する方針があります。それは、私がブラックリストに多くの努力を払わない立場に立つ。より成熟したロール&許可の概念を使用すると、あなたのブラックリストとホワイトリストを細かく細かくすることさえできます。


UPDATE: 私はあなたがこれを見て推測:

私はタグレベルでホワイトリストをstrip_tagsが、属性上のすべてを受け入れないポイントを得ましたレベル。興味深いことに、HTMLpurifierは属性レベルでホワイトリストを作成するようです。ありがとう、ここで良い学習だった。

+2

'strip_tags'は危険な属性から保護できません。タグが許可されている限り、属性には全く触れません。 – Brian

2

Rubyで、私はNokogiriphp version)を使ってHTMLコンテンツを解析しています。ユーザーのデータを解析し、不要なタグや属性を削除してテキストに変換することができます。

phpQuery - 別のパーサー。

PHPにはstrip_tagsという機能があります。

それともてmanualyすべての属性を削除することができます。

$dom = new DOMDocument; 
$dom -> loadHTML($html); 
$xpath = new DOMXPath($dom); 
$nodes = $xpath -> query("//*[@style]"); // all elements with style attribute 
foreach ($nodes as $node) { 
    // remove or do what you want 
    $node -> removeAttribute("style"); 
} 
echo $dom -> saveHTML(); 
+0

'DOMDocument'はHTML5で動作しますか? – Brian

+0

@ブライアン彼は仕事ですが、良くありません。より良い使い方https://github.com/html5lib/html5lib-php – ostapische

1

WdHTMLParserクラスを参照してください。私は私のフォーラムにこのクラスを使用します。 WdHTMLParserと

サンプル:

このクラスは、アレイにHTMLを解析:

<div> 
    <span> 
     <br /> 
     <span> 
     un bout de texte 
     </span> 
     <input type="text" /> 
    </span> 
</div> 

アレイ:私は、このクラスを使用するHTML

Array (
[0] => Array (
    [name] => div 
    [args] => Array() 
    [children] => Array (
    [0] => Array (
    [name] => span 
    [args] => Array() 
    [children] => Array (
    [0] => Array (
     [name] => br 
     [args] => Array() 
    ) 
    [1] => Array (
     [name] => span 
     [args] => Array() 
     [children] => Array (
     [0] => un bout de texte 
    ) 
    ) 
    [2] => Array (
     [name] => input 
     [args] => Array (
     [type] => text 
    ) 
    ) 
    ) 
    ) 
) 
) 
) 

WdHTMLParser配列配列をHTMLに変換する私のウェブサイト。

  • voyageWdHTML_allowattr:これらの属性が許可されます。

  • voyageWdHTML_allowtag:これらのタグは許可されます。

  • voyageWdHTML_special:独自のルールを作成します。実際には、各リンクに「_blank」を追加します。また、<br>に変更してください。新しいタグ(\ n)の前のタグに置き換えてください。

  • fix_javascript:この機能を有効/無効にすることはできますが、無意味です。

サンプルPHP:

<?php 
include "WdHTMLParser.php"; 
include "parser.php"; 

list($erreur, $message) = (new Parser())->parseBadHTML("<div> 
    <span> 
     <a onclick=\"alert('Hacked ! :'(');\">Check javascript</a> 
     <script>alert(\"lol\");</script> 
    </span> 
</div>"); 

if ($erreur) { 
    die("Error : ".$message); 
} 

echo $message; 

出力:

<div> 
    <span> 
     <a target="_blank">Check javascript</a> 
     <pre>alert("lol");</pre> 
    </span> 
</div> 

マイParserクラス:

<?php 
class Parser { 
    //private function fix_javascript(&$message) { } 

    private function voyageWdHTML_args($tab_args, $objname) { 
     $html = ""; 
     foreach ($tab_args as $attr => $valeur) { 
      if ($valeur !== null && $this->voyageWdHTML_allowattr($attr)) { 
       $html .= " $attr=\"".htmlentities($valeur)."\""; 
      } 
     } 
     return $html; 
    } 

    private function voyageWdHTML_allowattr($attr) { 
     return in_array($attr, array("align", "face", "size", "href", "title", "target", "src", "color", "style", 
            "data-class", "data-format")); 
    } 

    private function voyageWdHTML_allowtag($name) { 
     return in_array($name, array("br", "b", "i", "u", "strike", "sub", "sup", "div", "ol", "ul", "li", "font", "span", "code", 
            "hr", "blockquote", "cite", "a", "img", "p", "pre", "h6", "h5", "h4", "h3", "h2", "h1")); 
    } 

    private function voyageWdHTML_special(&$obj) { 
     if ($obj["name"] == "a") { $obj["args"]["target"] = "_blank"; } 
     if ($obj["name"] == "pre") { 
      array_filter($obj["children"], function (&$var) { 
       if (is_string($var)) { return true; } 
       if ($var["name"] == "br") { $var = "\n"; return true; } 
       return false; 
      }); 
     } 
    } 

    private function voyageWdHTML($tableau, $lvl = 0) { 
     $html = ""; 
     foreach ($tableau as $obj) { 
      if (is_array($obj)) { 
       if (!$this->voyageWdHTML_allowtag($obj["name"])) { 
        $obj["name"] = "pre"; 
        if (!isset($obj["children"])) { 
         $obj["children"] = array(); 
        } 
       } 
       if (isset($obj["children"])) { 
        $this->voyageWdHTML_special($obj); 
        $html .= "<{$obj["name"]}{$this->voyageWdHTML_args($obj["args"], $obj["name"])}>{$this->voyageWdHTML($obj["children"], $lvl+1)}</{$obj["name"]}>"; 
       } else { 
        $html .= "<{$obj["name"]}>"; 
       } 
      } else { 
       $html .= $obj; 
      } 
     } 
     return $html; 
    } 

    public function parseBadHTML($message) { 
     $WdHTMLParser = new WdHTMLParser(); 
     $message = str_replace(array("<br>", "<hr>"), array("<br/>", "<hr/>"), $message); 
     $tableau = $WdHTMLParser->parse($message); 

     if ($WdHTMLParser->malformed) { 
      $retour = $WdHTMLParser->error; 
     } else { 
      $retour = $this->voyageWdHTML($tableau); 

      //$this->fix_javascript($retour);// To make sur 
     } 

     return array($WdHTMLParser->malformed, $retour); 
    } 
} 

シュール利用するためにWdHTMLParserクラス

<?php 
class WdHTMLParser { 
    private $encoding; 
    private $matches; 
    private $escaped; 
    private $opened = array(); 
    public $malformed; 
    public function parse($html, $namespace = NULL, $encoding = 'utf-8') { 
     $this->malformed = false; 
     $this->encoding = $encoding; 
     $html   = $this->escapeSpecials($html); 
     $this->matches = preg_split('#<(/?)' . $namespace . '([^>]*)>#', $html, -1, PREG_SPLIT_DELIM_CAPTURE); 
     $tree   = $this->buildTree(); 
     if ($this->escaped) { 
      $tree = $this->unescapeSpecials($tree); 
     } 
     return $tree; 
    } 
    private function escapeSpecials($html) { 
     $html = preg_replace_callback('#<\!--.+-->#sU', array($this, 'escapeSpecials_callback'), $html); 
     $html = preg_replace_callback('#<\?.+\?>#sU', array($this, 'escapeSpecials_callback'), $html); 
     return $html; 
    } 
    private function escapeSpecials_callback($m) { 
     $this->escaped = true; 
     $text   = $m[0]; 
     $text   = str_replace(array('<', '>'), array("\x01", "\x02"), $text); 
     return $text; 
    } 
    private function unescapeSpecials($tree) { 
     return is_array($tree) ? array_map(array($this, 'unescapeSpecials'), $tree) : str_replace(array("\x01", "\x02"), array('<', '>'), $tree); 
    } 
    private function buildTree() { 
     $nodes = array(); 
     $i  = 0; 
     $text = NULL; 
     while (($value = array_shift($this->matches)) !== NULL) { 
      switch ($i++ % 3) { 
       case 0: { 
        if (trim($value)) { 
         $nodes[] = $value; 
        } 
       } 
        break; 
       case 1: { 
        $closing = ($value == '/'); 
       } 
        break; 
       case 2: { 
        if (substr($value, -1, 1) == '/') { 
         $nodes[] = $this->parseMarkup(substr($value, 0, -1)); 
        } else if ($closing) { 
         $open = array_pop($this->opened); 
         if ($value != $open) { 
          $this->error($value, $open); 
         } 
         return $nodes; 
        } else { 
         $node    = $this->parseMarkup($value); 
         $this->opened[] = $node['name']; 
         $node['children'] = $this->buildTree($this->matches); 
         $nodes[]   = $node; 
        } 
       } 
      } 
     } 
     return $nodes; 
    } 
    public function parseMarkup($markup) { 
     preg_match('#^[^\s]+#', $markup, $matches); 
     $name = $matches[0]; 
     preg_match_all('#\s+([^=]+)\s*=\s*"([^"]+)"#', $markup, $matches, PREG_SET_ORDER); 
     $args = array(); 
     foreach ($matches as $m) { 
      $args[$m[1]] = html_entity_decode($m[2], ENT_QUOTES, $this->encoding); 
     } 
     return array('name' => $name, 'args' => $args); 
    } 
    public function error($markup, $expected) { 
     $this->malformed = true; 
     printf('unexpected closing markup "%s", should be "%s"', $markup, $expected); 
    } 
} 

、あなたは、この機能(mybb.com)を使用することができます。

<?php 
class Parser { 
    private function fix_javascript(&$message) { 
     $js_array = array(
      "#(&\#(0*)106;?|&\#(0*)74;?|&\#x(0*)4a;?|&\#x(0*)6a;?|j)((&\#(0*)97;?|&\#(0*)65;?|a)(&\#(0*)118;?|&\#(0*)86;?|v)(&\#(0*)97;?|&\#(0*)65;?|a)(\s)?(&\#(0*)115;?|&\#(0*)83;?|s)(&\#(0*)99;?|&\#(0*)67;?|c)(&\#(0*)114;?|&\#(0*)82;?|r)(&\#(0*)105;?|&\#(0*)73;?|i)(&\#112;?|&\#(0*)80;?|p)(&\#(0*)116;?|&\#(0*)84;?|t)(&\#(0*)58;?|\:))#i", 
      "#(o)(nmouseover\s?=)#i", 
      "#(o)(nmouseout\s?=)#i", 
      "#(o)(nmousedown\s?=)#i", 
      "#(o)(nmousemove\s?=)#i", 
      "#(o)(nmouseup\s?=)#i", 
      "#(o)(nclick\s?=)#i", 
      "#(o)(ndblclick\s?=)#i", 
      "#(o)(nload\s?=)#i", 
      "#(o)(nsubmit\s?=)#i", 
      "#(o)(nblur\s?=)#i", 
      "#(o)(nchange\s?=)#i", 
      "#(o)(nfocus\s?=)#i", 
      "#(o)(nselect\s?=)#i", 
      "#(o)(nunload\s?=)#i", 
      "#(o)(nkeypress\s?=)#i" 
     ); 

     $message = preg_replace($js_array, "$1<b></b>$2$4", $message); 
    } 
} 
0

私はちょうどhtml5lib-のpythonを使用することにしました。これは私が思い付いたものです:

#!/usr/bin/env python 
import sys 
from xml.dom.minidom import Node 
import html5lib 
from html5lib import (HTMLParser, sanitizer, serializer, treebuilders, 
        treewalkers) 

parser = HTMLParser(tokenizer=sanitizer.HTMLSanitizer, 
        tree=treebuilders.getTreeBuilder("dom")) 
serializer = serializer.htmlserializer.HTMLSerializer(omit_optional_tags=False) 

document = parser.parse(sys.stdin.read(), encoding="utf-8") 
# find the <html> node 
for child in document.childNodes: 
    if child.nodeType == Node.ELEMENT_NODE and child.nodeName == 'html': 
     htmlNode = child 
# find the <body> node 
for child in htmlNode.childNodes: 
    if child.nodeType == Node.ELEMENT_NODE and child.nodeName == 'body': 
     bodyNode = child 
# serialize all children of the <body> node 
for child in bodyNode.childNodes: 
    stream = treewalkers.getTreeWalker("dom")(child) 
    sys.stdout.write(serializer.render(stream, encoding="utf-8")) 

例入力:

<script>alert("hax")</script> 
<p onload="alert('this is a dangerous attribute')"><b>hello,</b> world</p> 

出力例:

http://htmlpurifier.org/docs:私は個人的にこの正確な目的のためのHTML清浄機を使用

&lt;script&gt;alert("hax")&lt;/script&gt; 
<p><b>hello,</b> world</p> 
+0

編集:これはPython 2でのみ動作します。私はPython 3でも動作するバージョンを持っていますが、ちょっとハックされているので投稿しません。 – Brian

0

これはうまく動作し、すべてのタグと属性に合わせてカスタマイズすることができます。これまでのところ、このプラグインにはセキュリティ上の問題はありませんでした。

+0

HTML PurifierはまだHTML5をサポートしていません。 – Brian

+0

しかし、独自のタグと属性を定義することができます:) – morissette

+0

ここに例があります:https://gist.github.com/lluchs/3303693 – morissette

関連する問題