2013-07-17 6 views
5

Я хочу, чтобы иметь возможность принимать HTML от ненадежных пользователей и дезинфицировать его, чтобы я мог безопасно включать его в страницы моего сайта. Под этим я подразумеваю, что разметка не должна быть удалена или экранирована, но должна быть передана по существу без изменений, если она не содержит опасных тегов, таких как <script> или <iframe>, опасных атрибутов, таких как onload, или опасных свойств CSS, таких как фоновые URL-адреса. (По-видимому, некоторые старые IE будут выполнять javascript-URL в CSS?)Sanitizing untrusted HTML5

Обслуживание содержимого из другого домена, заключенного в iframe, не является хорошим вариантом, потому что нет способа заранее сказать, насколько высок iframe так что он всегда будет выглядеть уродливым для некоторых страниц.

Я заглянул в очиститель HTML, но похоже, что он еще не поддерживает HTML5. Я также изучил Google Caja, но я ищу решение, которое не использует скрипты.

Кто-нибудь знает библиотеку, которая выполнит это? PHP предпочтительнее, но нищие не могут быть выбраны.

+0

Вы можете попробовать [sanisser raxan data sanitizer] (https://searchcode.com/codesearch/view/2955473/) – Gunaseelan

+2

Вопросы, предлагающие нам рекомендовать или находить инструмент, библиотеку или любимый ресурс вне сайта, не соответствуют теме Stack Overflow, поскольку они склонны привлекать упрямые ответы и спам. Вместо этого опишите проблему и то, что было сделано до сих пор, чтобы ее решить. –

ответ

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-очиститель, вероятно, является самым современным и хорошо подходящим проектом (и с открытым исходным кодом), вы все равно должны использовать его и, возможно, внести коррективы, если они вам действительно понадобятся.

Вы можете проверить одно из следующих действий, а также:

  • kses - де-факто стандартом, нашли путь в WordPress, а
  • htmLawed - AN далее развитые kses
  • PHP Input Filter - может фильтровать теги и атрибуты

Хотя вам также необходимо убедиться, что ваш собственный макет страницы не попал в цель, включая res из-за не закрытых тегов.

2

Может быть, лучше пойти на другой подход? Как рассказать им, что они могут использовать?

В этом случае вы можете использовать использование strip_tags. Это будет проще и намного более контролируемым. Очень легко продлить в будущем, а также

+1

> Эта функция не изменяет никаких атрибутов в тегах, которые вы разрешаете использовать allowable_tags, включая атрибуты стиля и onmouseover, которые может оскорбительный пользователь может злоупотреблять при публикации текста, который будет показан другим пользователям. – Brian

6

Черный подход к листингу ставит вас под давлением обновления. Поэтому каждый раз, когда браузеры начинают поддерживать новые стандарты, вы ДОЛЖНЫ нарисовать ваш инструмент для дезинфекции на одном уровне. Такие изменения происходят чаще, чем вы думаете.

Белые листинга (что достигается strip_tags с четко определенными исключениями), которые вызывают усадки для ваших пользователей, но помещают вас на сайт сохранения.

На моих собственных сайтах у меня есть политика применения черного списка на страницах для очень надежных пользователей (таких как администраторы) и белого списка на всех других страницах. Это ставит меня в положение, чтобы не прикладывать много усилий в черный список. С более зрелой ролью & концепции разрешения вы можете даже тонко разделить ваши черные списки и белые списки.


UPDATE: Я думаю, вы посмотрите на это:

Я получил точку, что strip_tags белых списков на уровне тегов но принимать все на атрибуте уровень. Интересно, что HTMLpurifier, похоже, выполняет «белый список» на уровне атрибутов. Спасибо, было приятно учиться здесь.

+2

'strip_tags' не может защитить от опасных атрибутов. Пока тег разрешен, он вообще не касается атрибутов. – Brian

2

На Ruby я использую Nokogiri (php version) для анализа содержимого HTML. Вы можете анализировать данные пользователя и удалять ненужные теги или атрибуты, а затем преобразовывать их в текст.

phpQuery - еще один парсер.

И в PHP есть функция strip_tags.

Или вы можете удалить все Мануалы атрибуты:

$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

@Brian он работает, но не хорошо. Лучше использовать 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> 

Массив:

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

Я использую этот класс на мой сайт для преобразования массива в 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-питона. Это то, что я придумал:

#!/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> 

Пример вывода:

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

Редактировать: Это работает только в Python 2. У меня есть версия, которая работает и в Python 3, но я не буду публиковать ее, потому что она немного хакерская , – Brian

0

Я лично использую HTML очистителя для точной цели:

http://htmlpurifier.org/docs

Он хорошо работает и позволяет настраивать все теги и атрибуты. До сих пор у меня не было проблем с безопасностью с этим плагином.

+0

HTML Очиститель еще не поддерживает HTML5. – Brian

+0

Но это позволяет вам определить свои собственные теги и атрибуты :) – morissette

+0

Вот пример: https://gist.github.com/lluchs/3303693 – morissette

 Смежные вопросы

  • Нет связанных вопросов^_^