Parse HTML Safely

jQuery.parseHTML

给定一段HTML代码, 如何将其转化为DOM树以便处理?

如果使用jQuery, 可以使用其$.parseHTML 方法将HTML代码转化为DOM树.

var markup = '<p>' +
        '<img src="http://note.youdao.com/styles/images/icon-1.png?96" data-media-type="image">' +
        '<script type="text/javascript">' +
            'document.onclick=function(){console.log("click");};' +
            'window.alert("hello");' +
            'document.write("hello");' +
        '</script>' +
    '</p>',
    domArray = $.parseHTML(markup);

    window.console.log(domArray);

翻看jQuery的源代码, 其parse HTML的原理与以下代码一致:

    /**
     * @param {String} markup
     * @param {Document} [context]
     * @return {Array}
     */
var parseHTMLWithDiv = function (markup, context) {
        context = context || document;

        var wrapper = context.createElement('div'),
            domArray = [],
            index,
            len;

        wrapper.innerHTML = markup;
        len = wrapper.childNodes.length;
        for (index = 0; index < len; index++) {
            domArray.push(wrapper.childNodes[index]);
        }

        return domArray;
    };

亦可以在将HTML内容放置在隐藏的iframe中进行parse.

/**
 * @param {String} markup
 * @param {Document} [context]
 * @return {Array}
 */
var parseHTMLWithIframe = function (markup, context) {
    context = context || document;
    var iframe = context.createElement('iframe'),
        body,
        index,
        len,
        domArray = [];
    iframe.src = '';
    iframe.style.display = 'none';
    context.body.appendChild(iframe);
    body = iframe.contentDocument.body;
    body.innerHTML = markup;
    len = body.childNodes.length;

    for (index = 0; index < len; index++) {
        domArray.push(body.childNodes[index]);
    }
    context.body.removeChild(iframe);
    return domArray;
}

在parse HTML过程中, 如果仔细观察, 可以发现以下几点:

  1. HTML代码中的script不会被执行

    • 保证安全
  2. 浏览器会自动发出图片src的请求, 可以预加载图片
    • 使用div时, 图片会预加载
    • 使用iframe时, 图片加载请求会发出, 但会被取消

DOMParser

如果被parse过后的HTML代码并不需要注入到页面上, parse HTML过程中浏览器自动发出图片 src请求就会占用网络请求等资源, 这是不完美的.

该如何做才不会让浏览器自动发出图片src请求呢?

jQuery中有一个与$.parseHTML()类似的方法, 叫做$.parseXML(), 用于parse xml. 查看源码:

// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
    var xml, tmp;
    if ( !data || typeof data !== "string" ) {
        return null;
    }

    // Support: IE9
    try {
        tmp = new DOMParser();
        xml = tmp.parseFromString( data, "text/xml" );
    } catch ( e ) {
        xml = undefined;
    }

    if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
        jQuery.error( "Invalid XML: " + data );
    }
    return xml;
};

jQuery 使用了 DOMParser 对XML文档进行parse. DOMParser 不但能够parse XML文档, 还 能 parse HTML文档.

enum SupportedType {
  "text/html",
  "text/xml",
  "application/xml",
  "application/xhtml+xml",
  "image/svg+xml"
};

[Constructor]
interface DOMParser {
  Document parseFromString(DOMString str, SupportedType type);
};

使用 DOMParser 分析HTML文档时, 浏览器不会自动发出图片src的请求. 在不支持DOMParser的浏览器中, 有一个替代方案: DOMImplementation.createHTMLDocument

/**
 * There are two ways to parse html snippet:
 * 1. parse html in a virtual Document/DOMParser object.
 * 2. create a `div` element as wrapper and set html as its innerHTML.
 *
 * The 1st way can prevent loading images that in the html and is safer.
 *
 * NOTE:  This function does not support ie8 and ie8-
 *
 * @param {String} markup the html string that can be set as the  innserHTML
 * @param {Document} [context]
 * of <body/>
 * @return {Document} if returned value is null, you can follow the 2ed way.
 */
function parseHTML(markup, context) {
    var doc,
        parser,
        win;

    context = context || document;

    if (context.implementation &&
            context.implementation.createHTMLDocument) {
        doc = context.implementation.createHTMLDocument();
        doc.body.innerHTML = markup;
        return doc;
    }

    win = context.defaultView || window;
    if (win.DOMParser) {
        parser = new win.DOMParser();
        try {
            doc = parser.parseFromString('', 'text/html');
        } catch (ex) {
            // do nothing
        }
        if (doc) {
            doc.body.innerHTML = markup;
            return doc;
        }
    }

};

Reference

  1. https://code.google.com/p/google-caja/issues/detail?id=1823
  2. http://api.jquery.com/jquery.parsehtml/
  3. https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
  4. http://domparsing.spec.whatwg.org/
  5. https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation.createHTMLDocument

Parse HTML Safely

时间: 2024-10-24 13:13:43

Parse HTML Safely的相关文章

WebSocket Security (WebSocket 安全)

WebSocket Security The WebSocket protocol is a young technology, and brings with it some risks. Decades of experience have taught the web community some best practices around HTTP security, but the security best practices in the WebSocket world aren’

C#中Convert和parse的区别

Convert.ToInt32()与int.Parse()的区别(1)这两个方法的最大不同是它们对null值的处理方法: Convert.ToInt32(null)会返回0而不会产生任何异常,但int.Parse(null)则会产生异常. 没搞清楚Convert.ToInt32和int.Parse()的细细微区别时千万别乱用,否则可能会产生无法预料的结果,举例来说:假如从url中取一个参数page的值,我们知道这个值是一个int,所以即可以用Convert.ToInt32(Request.Que

Int.Parse与ConvertToInt的区别

最近在公司做一个项目的时候,在某次 跟组长讨论的时候.对一个简单的线发生了一些讨论.感觉挺有收获的.现在来总结一下. 在代码开发的时候,底层代码,不要写try-catch.因为写异常处理之后,代码的执行效率会大大的降低.底层的代码,不写异常,但是要经过严格的测试,确保正确.而对于界面层来说,必须接收异常.程序崩溃是非常不友好的.而且也降低了用户对你程序的信任. 对几个小问题的整理: 1.try-catch是否影响效率 自己简单测试了一下,感觉不到效率有什么变化. 然后查找一系列相关博客.发现了一

JSON.parse()和eval()的区别

json格式非常受欢迎,而解析json的方式通常用JSON.parse()但是eval()方法也可以解析,这两者之间有什么区别呢? JSON.parse()之可以解析json格式的数据,并且会对要解析的字符串进行格式检查,如果格式不正确则不进行解析,而eval()则可以解析任何字符串,eval是不安全的. 比如下面的字符串: var str = 'alert(1000.toString())'; eval(str); JSON.parse(str); 用eval可以解析,并且会弹出对话框,而用J

记一次django的诡异报错 Could not parse the remainder: &#39;=&#39; from &#39;=&#39;

如题,一个展示日志的功能,调用该模板文件时报错,Could not parse the remainder: '=' from '='.这行模板语言在上面出现过同样的,仅仅是改了'<='右端的整数大小,而且该有的标签也不少. 百般思考无解,stackoverflow和百度都没答案.后来重新看了下开发文档,发现是Django的版本不对,开发时用的1.8.2,我部署时用的1.11.5.重新安装低版本的django后问题解决.但原因仍待查. 记一次django的诡异报错 Could not parse

java parse 带英文单词的日期字符串 转 date (转化新浪微博api返回的时间)

拂晓风起 专注前端技术cocos2d.js.flash.html5,联系:[email protected].如果读者要找腾讯工作机会,请不吝推荐简历. 博客园 首页 新闻 新随笔 联系 管理 订阅 随笔- 227  文章- 0  评论- 336 java parse 带英文单词的日期字符串 转 date (转化新浪微博api返回的时间) 大家一般很少格式化或者parse带有Sun Nov等英文单词的字符串. 如果格式化英文月份的字符串,记得带上Locale.US参数,否则,JRE会按照当前地区

【java】Date与String之间的转换:java.text.SimpleDateFormat、public Date parse(String source) throws ParseException和public final String format(Date date)

1 package 日期日历类; 2 3 import java.text.ParseException; 4 import java.text.SimpleDateFormat; 5 import java.util.Date; 6 7 public class TestDate { 8 public static void main(String[] args) { 9 Date date=new java.util.Date(); 10 System.out.println(date);/

JSON.parse()和JSON.stringify()

JSON.parse(): 把json格式的字符 解析成json对象. 例如: var str = '{'a':1, 'b': 2}';  JSON.parse(str ) 结果为: Object:  {a:1 , b:2} JSON.stringify() : 把一个对象解析成字符串. 例如: var a = {a:1,b:2}  JSON.stringify(a) //结果为  '{'a':1,'b':2}' 应用:可以使用这两种方法,处理cookie等数据,转化成对象易于读取,字符串用于存

js中json处理总结之JSON.parse

踩过的坑都将成为路上的风景.队友在cookie中已存以下值: address_info {"address_name":"人民大会堂","...lng":1,"address_lat":1} 仔细观摩,并无发现任何不妥,只是一种简简单单的json格式字符串而已. 但在前台调用时,百试不爽,屡屡出错,错误代码如下,一直显示undefined var address_info = getCookie('address_info')