2013-07-17 27 views
5

Tôi muốn có thể chấp nhận HTML từ những người dùng không đáng tin cậy và vệ sinh nó để tôi có thể đưa nó vào các trang trên trang web của mình một cách an toàn. Bằng cách này, tôi có nghĩa là đánh dấu không được lột hoặc thoát, nhưng phải được chuyển qua về cơ bản không thay đổi trừ khi nó chứa các thẻ nguy hiểm như <script> hoặc , các thuộc tính nguy hiểm như onload hoặc các thuộc tính CSS nguy hiểm như URL nền. (Dường như một số IE cũ hơn sẽ thực thi các URL javascript trong CSS?)Vệ sinh không tin cậy HTML5

Cung cấp nội dung từ một miền khác, được bao quanh trong khung nội tuyến, không phải là lựa chọn tốt vì không có cách nào để biết trước iframe phải vì vậy nó sẽ luôn trông xấu xí đối với một số trang.

Tôi đã xem xét Trình lọc HTML, nhưng có vẻ như nó chưa hỗ trợ HTML5. Tôi cũng đã xem xét Google Caja, nhưng tôi đang tìm một giải pháp không sử dụng tập lệnh.

Có ai biết thư viện sẽ thực hiện việc này không? PHP được ưa thích, nhưng người ăn xin không thể chọn lựa.

+0

Bạn có thể thử [raxan trình khử trùng dữ liệu] (https://searchcode.com/codesearch/view/2955473/) – Gunaseelan

+2

Câu hỏi yêu cầu chúng tôi đề xuất hoặc tìm một công cụ, thư viện hoặc tài nguyên ngoài trang web yêu thích không có chủ đề Stack Overflow vì chúng có xu hướng thu hút các câu trả lời và spam có ý kiến. Thay vào đó, mô tả vấn đề và những gì đã được thực hiện cho đến nay để giải quyết nó. –

Trả lời

2

Bạn có thể có thể làm điều gì đó dọc theo dòng:

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); 

... nhưng sau đó một lần nữa: bạn có regexes, bây giờ bạn có hai vấn đề - điều này có thể loại bỏ hơn muốn và để lại hơn cũng muốn.

Nhưng vì Bộ lọc HTML có lẽ là dự án hiện đại và phù hợp nhất (và nguồn mở), bạn vẫn nên sử dụng dự án đó và có thể điều chỉnh nếu bạn thực sự cần chúng.

Bạn có thể kiểm tra một trong những điều sau đây cũng như:

  • kses - tiêu chuẩn de facto, tìm ra cách vào wordpress cũng
  • htmLawed - một kses tiếp tục phát triển
  • PHP Input Filter - có thể lọc thẻ và thuộc tính

Mặc dù bạn cũng phải đảm bảo rằng bố cục trang của riêng bạn không bị ảnh hưởng trong việc bao gồm cả ults do không đóng thẻ.

2

Có thể bạn nên tiếp cận một cách tiếp cận khác? Làm thế nào để nói cho họ biết những gì họ có thể sử dụng?

Trong trường hợp đó, bạn có thể sử dụng strip_tags. Nó sẽ dễ dàng hơn và dễ điều khiển hơn theo cách này. Rất dễ dàng để mở rộng trong tương lai cũng như

+1

> Chức năng này không sửa đổi bất kỳ thuộc tính nào trên thẻ mà bạn cho phép sử dụng allowable_tags, bao gồm thuộc tính kiểu và thuộc tính onmouseover mà người dùng tinh nghịch có thể lạm dụng khi đăng văn bản sẽ được hiển thị cho người dùng khác. – Brian

6

Cách tiếp cận danh sách đen đặt bạn dưới áp lực nâng cấp. Vì vậy, mỗi khi trình duyệt bắt đầu hỗ trợ các tiêu chuẩn mới, bạn PHẢI rút công cụ vệ sinh của bạn xuống cùng cấp. Những thay đổi như vậy xảy ra thường xuyên hơn bạn nghĩ.

Danh sách trắng (được thực hiện bởi strip_tags với ngoại lệ được xác định rõ ràng) về nguyên nhân co rút tùy chọn cho người dùng của bạn, nhưng đặt bạn vào trang lưu.

Trên trang web của riêng mình, tôi có chính sách áp dụng danh sách đen trên trang cho người dùng rất đáng tin cậy (chẳng hạn như quản trị viên) và danh sách trắng trên tất cả các trang khác. Điều đó đặt tôi vào vị trí để không đặt nhiều nỗ lực vào danh sách đen. Với vai trò trưởng thành hơn, & các khái niệm quyền bạn thậm chí có thể làm mịn các danh sách đen và danh sách trắng của bạn.


UPDATE: Tôi đoán bạn tìm kiếm này:

Tôi có quan điểm rằng strip_tags danh sách cho phép trên mức thẻ nhưng không chấp nhận tất cả mọi thứ trên thuộc tính cấp độ. HTMLpurifier thú vị dường như làm các danh sách trắng trên cấp thuộc tính. Cảm ơn, là một học tập tốt đẹp ở đây.

+2

'strip_tags' không thể bảo vệ khỏi các thuộc tính nguy hiểm. Miễn là thẻ được cho phép, nó không chạm vào các thuộc tính chút nào. – Brian

2

Trên Ruby tôi đang sử dụng Nokogiri (php version) để phân tích nội dung HTML. Bạn có thể phân tích dữ liệu của người dùng và xóa các thẻ hoặc thuộc tính không cần thiết, sau đó chuyển đổi nó thành văn bản.

phpQuery - trình phân tích cú pháp khác.

Và trong PHP, có chức năng strip_tags.

Hoặc bạn manualy có thể loại bỏ tất cả các thuộc tính:

$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' có hoạt động với HTML5 không? – Brian

+0

@ Brian anh ấy làm việc, nhưng không tốt. Sử dụng tốt hơn https://github.com/html5lib/html5lib-php – ostapische

1

Xem WdHTMLParser lớp. Tôi sử dụng lớp này cho diễn đàn của tôi.

mẫu với WdHTMLParser:

Lớp này phân tích cú pháp HTML để một mảng:

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

Array:

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 mảng sang HTML

tôi sử dụng lớp này trên trang web của tôi để chuyển đổi mảng thành HTML.

  • voyageWdHTML_allowattr: Những thuộc tính này sẽ được cho phép.

  • chuyến điWdHTML_allowtag: Các thẻ này sẽ được cho phép.

  • voyageWdHTML_special: Tạo quy tắc của riêng bạn. Trên thực tế, tôi thêm "_blank" vào mỗi liên kết. Và thay thế <br> thành dòng mới (\ n) trong thẻ trước.

  • fix_javascript: Bạn có thể bật/tắt chức năng này, nhưng nó vô ích.

mẫu 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; 

Output:

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

My Parser lớp:

<?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 lớp

<?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); 
    } 
} 

Tận dụng sur, bạn có thể sử dụng chức năng này (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

tôi quyết định chỉ sử dụng html5lib-python. Đây là những gì tôi đã đưa ra:

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

Ví dụ đầu vào:

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

Ví dụ đầu ra:

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

Chỉnh sửa: Tính năng này chỉ hoạt động trong Python 2. Tôi cũng có phiên bản hoạt động trong Python 3, nhưng tôi sẽ không đăng nó vì nó hơi bị hack . – Brian

0

Cá nhân tôi sử dụng máy lọc HTML cho mục đích chính xác này:

http://htmlpurifier.org/docs

Nó hoạt động tốt và cho phép bạn tùy chỉnh xuống mọi thẻ và thuộc tính. Cho đến nay tôi đã không có vấn đề bảo mật với plugin này.

+0

Trình lọc HTML không hỗ trợ HTML5. – Brian

+0

Nhưng nó cho phép bạn xác định các thẻ và thuộc tính của riêng bạn :) – morissette

+0

Đây là một ví dụ: https://gist.github.com/lluchs/3303693 – morissette

Các vấn đề liên quan