前者安全,贰零壹肆Ali学校招聘前端在线测验标题

JavaScript 防 http 劫持与 XSS

2016/08/17 · JavaScript
· 1 评论 ·
http劫持, X
DNS劫持,
XSS,
安全

正文小编: 伯乐在线 –
chokcoco
。未经小编许可,禁止转发!
迎接出席伯乐在线 专辑小编。

用作前端,平素以来都通晓HTTP劫持XSS跨站脚本(Cross-site
scripting)、CSRF跨站请求伪造(克罗丝-site request
forgery)。可是从来都未曾浓密钻研过,前些日子同事的分享会偶然提及,笔者也对这一块很感兴趣,便浓厚钻研了一番。

近些年用 JavaScript 写了一个零件,能够在前者层面防御部分 HTTP 威迫与 XSS。

自然,防御那个威迫最好的艺术恐怕从后端动手,前端能做的实在太少。而且由于源码的展露,攻击者很简单绕过大家的守卫手段。但是这不代表大家去探听那块的连锁文化是没意义的,本文的很多方法,用在其余地点也是大有成效。

已上传来 Github
– httphijack.js ,欢迎感兴趣看看顺手点个
star ,本文示例代码,防患方法在组件源码中皆可找到。

接下去进入正文。

作为前端,平素以来都知道HTTP劫持XSS跨站脚本(Cross-site
scripting)、CSRF跨站请求伪造(克罗斯-site request
forgery)。不过平素都不曾尖锐研究过,前些日子同事的分享会偶然提及,小编也对这一块很感兴趣,便深远研究了一番。

一 、JavaScript有哪些措施能一下子就解决了跨主域难点?

一、概述

HTTP劫持、DNS劫持与XSS

先简单讲讲怎么着是 HTTP 威逼与 DNS 威胁。

多年来用
JavaScript 写了三个零部件,能够在前者层面防御部分 HTTP 威吓与 XSS。

a.
document.domain+iframe的设置
b.
动态创制script
c.
利用iframe和location.hash
d.
window.name达成的跨域数据传输
e. 使用HTML5
postMessage

f. 利用flash

attribute和property是平日被弄混的七个概念。

HTTP劫持

什么是HTTP勒迫呢,大部分场合是营业商HTTP威吓,当大家使用HTTP请求请求一个网站页面包车型地铁时候,网络运转商会在例行的数目流中插入精心设计的网络数据报文,让客户端(平常是浏览器)显示“错误”的数额,日常是一对弹窗,宣传性广告还是间接展示某网站的内容,我们应该都有遇到过。

当然,防御那个胁制最好的法子照旧从后端入手,前端能做的实在太少。而且由于源码的展露,攻击者很不难绕过大家的守卫手段。不过这不代表大家去打听那块的连锁文化是没意义的,本文的过多方法,用在别的方面也是大有功用。

JavaScript跨域:处于安全地点的考虑,不允许跨域调用别的页面包车型地铁对象。简单来讲,由于JavaScript同源策略的限定,a.com域名下的js十分的小概操作b.com或c.a.com域名下的靶子

简短来说,property则是JS代码里拜访的:

DNS劫持

DNS威迫正是经过勒迫了DNS服务器,通过某个手段取得某域名的分析记录控制权,进而修改此域名的辨析结果,导致对该域名的走访由原IP地址转入到修改后的钦赐IP,其结果便是对一定的网址不能够访问或访问的是假网址,从而完成窃取资料还是破坏原有正常劳动的指标。

DNS 勒迫就更过分了,简单说正是大家呼吁的是 
,直接被重定向了
,本文不会过多探讨这种景色。

已上盛传
Github
– httphijack.js ,欢迎感兴趣看看顺手点个
star ,本文示例代码,防备措施在组件源码中皆可找到。

详见:
 

document.getElementByTagName(‘my-element’).prop1 = ‘hello’;

XSS跨站脚本

XSS指的是攻击者漏洞,向 Web
页面中流入恶意代码,当用户浏览该页之时,注入的代码会被执行,从而达到攻击的异样目标。

至于这几个攻击怎么样转变,攻击者如何注入恶意代码到页面中本文不做研讨,只要知道如
HTTP 威逼 和 XSS
最终都以恶意代码在客户端,通常约等于用户浏览器端执行,本文将探究的正是若是注入已经存在,怎么着使用
Javascript 进行有效的前端防护。

接下去进入正文。

二 、以下关于Nodejs的传道, 正确的是(ACD)

attribute类似那种:

页面被内置 iframe 中,重定向 iframe

先来说说大家的页面被置于了 iframe
的动静。也便是,网络运维商为了尽可能地压缩植入广告对原本网站页面包车型客车熏陶,平时会因而把原有网站页面放置到多少个和原页面相同大小的
iframe 里面去,那么就足以经过这几个 iframe
来隔开分离广告代码对原来页面包车型大巴震慑。
美高梅开户网址 1

那种景色还比较好处理,大家只供给明白大家的页面是不是被嵌套在 iframe
中,借使是,则重定向外层页面到大家的平日页面即可。

那正是说有没有艺术知情大家的页面当前留存于 iframe
中吗?有的,就是 window.self 与 window.top 。

 

A: Nodejs是二个Javascript运转环境, 基于chrome V8引擎举办代码解析

B: Nodejs本身不是单线程的, 但我们的js代码是在单线程的环境中推行

C: 能够动用uncaughtException大概Domain捕获格外,
在那之中uncaughtException能够保持上下文

D: Nodejs高并发个性使其符合I/O密集型的施用

<my-element attr1=”cool” />

window.self

归来五个对准当前 window 对象的引用。

HTTP劫持、DNS劫持与XSS

先简单讲讲哪些是
HTTP 威迫与 DNS 劫持。

 

JS代码里拜访attribute的点子是getAttribute和setAttribute:

window.top

重返窗口种类中的最顶层窗口的引用。

对此非同源的域名,iframe 子页面不能通过 parent.location 或者top.location 得到具体的页面地址,不过足以写入 top.location
,也便是足以决定父页面包车型大巴跳转。

多少个天性分别能够又简写为 self 与 top,所以当发现大家的页面被嵌套在
iframe 时,能够重定向父级页面:

JavaScript

if (self != top) { // 大家的正规页面 var url = location.href; //
父级页面重定向 top.location = url; }

1
2
3
4
5
6
if (self != top) {
  // 我们的正常页面
  var url = location.href;
  // 父级页面重定向
  top.location = url;
}

 

HTTP劫持

何以是HTTP劫持呢,大部分情形是营业商HTTP威逼,当大家选取HTTP请求请求三个网站页面包车型地铁时候,互联网运营商会在日常的数量流中插入精心设计的网络数据报文,让客户端(经常是浏览器)显示“错误”的数码,平时是部分弹窗,宣传性广告仍然间接体现某网站的始末,我们应该都有相逢过。

③ 、关于HTTP再次回到码的说法,上面哪些是漏洞万分多的?(AB)

document.getElementByTagName(‘my-element’).setAttribute(‘attr1′,’Hello’);

行使白名单放行平常 iframe 嵌套

理所当然很多时候,只怕运行要求,大家的页面会被以各个法子推广,也有恐怕是健康作业须要被嵌套在
iframe 中,这些时候大家需求一个白名单或然黑名单,当大家的页面被嵌套在
iframe 中且父级页面域名存在白名单中,则不做重定向操作。

地方也说了,使用 top.location.href 是不能够得到父级页面包车型大巴 URubiconL
的,这时候,须求选取document.referrer

经过 document.referrer 能够得到跨域 iframe 父页面包车型大巴UPAJEROL。

JavaScript

// 建立白名单 var whiteList = [ ‘www.aaa.com’, ‘res.bbb.com’ ]; if
(self != top) { var // 使用 document.referrer 能够获得跨域 iframe
父页面包车型大巴 UTiggoL parentUrl = document.referrer, length = whiteList.length, i
= 0; for(; i<length; i++){ // 建立白名单正则 var reg = new
RegExp(whiteList[i],’i’); // 存在白名单中,放行
if(reg.test(parentUrl)){ return; } } // 大家的健康页面 var url =
location.href; // 父级页面重定向 top.location = url; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 建立白名单
var whiteList = [
  ‘www.aaa.com’,
  ‘res.bbb.com’
];
if (self != top) {
  var
    // 使用 document.referrer 可以拿到跨域 iframe 父页面的 URL
    parentUrl = document.referrer,
    length = whiteList.length,
    i = 0;
  for(; i<length; i++){
    // 建立白名单正则
    var reg = new RegExp(whiteList[i],’i’);
    // 存在白名单中,放行
    if(reg.test(parentUrl)){
      return;
    }
  }
  // 我们的正常页面
  var url = location.href;
  // 父级页面重定向
  top.location = url;
}

 

DNS劫持

DNS
勒迫正是经过威胁了 DNS
服务器,通过一些手段获取某域名的辨析记录控制权,进而修改此域名的剖析结果,导致对该域名的走访由原IP地址转入到修改后的内定IP,其结果正是对特定的网址不能够访问或访问的是假网址,从而完结窃取资料只怕损坏原有正平常衣服务的目标。

DNS
要挟比之 HTTP 胁制越发过分,简单说便是我们呼吁的是 
,直接被重定向了
,本文不会过多探究那种情景。

A: 302表示劳务器端网页未修改过,客户端可从浏览器缓存中获得内容

B: 404意味着能源尽管存在,但运转出错

C: 503为服务器负荷过高不可能响应请求

D: 传送数据过大也许引致413(请求实体过大)的谬误

document.getElementByTagName(‘my-element’).getAttribute(‘attr1′,’Hello’);

更改 U福睿斯L 参数绕过运行商标记

这么就完了啊?没有,我们就算重定向了父页面,不过在重定向的历程中,既然第一次可以嵌套,那么那1回重定向的进度中页面可能又被
iframe 嵌套了,真尼玛蛋疼。

本来运维商那种威吓平常也是有迹可循,最健康的招数是在页面 U路虎极光L
中安装二个参数,例如
 ,其中 iframe_hijack_redirected=1 表示页面已经被吓唬过了,就不再嵌套
iframe 了。所以依照这一个性情,我们得以改写我们的 U凯雷德L
,使之看上去已经被恐吓了:

JavaScript

var flag = ‘iframe_hijack_redirected’; // 当前页面存在于七个 iframe 中
// 此处须要建立叁个白名单匹配规则,白名单私下认可放行 if (self != top) { var
// 使用 document.referrer 能够得到跨域 iframe 父页面包车型客车 U陆风X8L parentUrl =
document.referrer, length = whiteList.length, i = 0; for(; i<length;
i++){ // 建立白名单正则 var reg = new RegExp(whiteList[i],’i’); //
存在白名单中,放行 if(reg.test(parentUrl)){ return; } } var url =
location.href; var parts = url.split(‘#’); if (location.search) {
parts[0] += ‘&’ + flag + ‘=1’; } else { parts[0] += ‘?’ + flag +
‘=1’; } try { console.log(‘页面被置于iframe中:’, url); top.location.href
= parts.join(‘#’); } catch (e) {} }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var flag = ‘iframe_hijack_redirected’;
// 当前页面存在于一个 iframe 中
// 此处需要建立一个白名单匹配规则,白名单默认放行
if (self != top) {
  var
    // 使用 document.referrer 可以拿到跨域 iframe 父页面的 URL
    parentUrl = document.referrer,
    length = whiteList.length,
    i = 0;
  for(; i<length; i++){
    // 建立白名单正则
    var reg = new RegExp(whiteList[i],’i’);
    // 存在白名单中,放行
    if(reg.test(parentUrl)){
      return;
    }
  }
  var url = location.href;
  var parts = url.split(‘#’);
  if (location.search) {
    parts[0] += ‘&’ + flag + ‘=1’;
  } else {
    parts[0] += ‘?’ + flag + ‘=1’;
  }
  try {
    console.log(‘页面被嵌入iframe中:’, url);
    top.location.href = parts.join(‘#’);
  } catch (e) {}
}

理所当然,假若那些参数一改,防嵌套的代码就失效了。所以我们还索要树立五个汇报系统,当发现页面被嵌套时,发送四个截留上报,就算重定向退步,也能够明白页面嵌入
iframe 中的 ULANDL,依照分析这一个 U卡宴L
,不断拉长大家的幸免手段,这一个后文少禽提及。

XSS跨站脚本

XSS指的是攻击者利用漏洞,向
Web
页面中流入恶意代码,当用户浏览该页之时,注入的代码会被实施,从而实现攻击的独特指标。

关于那一个攻击如何转移,攻击者怎样注入恶意代码到页面中本文不做探究,只要精通如
HTTP 恐吓 和 XSS
最后都以恶意代码在客户端,日常也便是用户浏览器端执行,本文将探究的正是只要注入已经存在,怎么样运用
Javascript 实行中用的前端防护。

 

二、区别

内联事件及内联脚本拦截

在 XSS 中,其实能够注入脚本的点子格外的多,越发是 HTML5
出来之后,一不留神,许多的新标签都得以用于注入可进行脚本。

列出有个别比较宽泛的流入方式:

  1. <a href="javascript:alert(1)" ></a>
  2. <iframe src="javascript:alert(1)" />
  3. <img src='x' onerror="alert(1)" />
  4. <video src='x' onerror="alert(1)" ></video>
  5. <div onclick="alert(1)" onmouseover="alert(2)" ><div>

除了一些未列出来的卓殊少见生僻的注入格局,超越59%都以 javascript:... 及内联事件 on*

我们即使注入已经发生,那么有没有方法堵住那一个内联事件与内联脚本的推行吗?

对此地点列出的 (1) (5)
,那种要求用户点击或然实施某种事件之后才实施的脚本,大家是有方法进行防卫的。

 

4.1 说说nodejs的异步I/O是什么。

超过半数气象下,两者是均等的。在web标准中,平常会规定某attribute“反射”了同名的property。可是不一样的事态依旧广大的。

浏览器事件模型

此处说能够阻挡,涉及到了事件模型连带的原理。

我们都精晓,标准浏览器事件模型存在四个级次:

  • 破获阶段
  • 指标阶段
  • 冒泡阶段

对于1个这么 <a href="javascript:alert(222)" ></a> 的 a
标签而言,真正触发成分 alert(222) 是处于点击事件的靶子阶段。

See the Pen EyrjkG by Chokcoco
(@Chokcoco) on
CodePen.

点击上边的 click me ,先弹出 111 ,后弹出 222。

那么,大家只要求在点击事件模型的捕获阶段对标签内 javascript:... 的内容建立主要字黑名单,实行过滤审查,就足以成功大家想要的阻碍效果。

对于 on*
类内联事件也是同理,只是对于那类事件太多,大家不可能手动枚举,可以选择代码自动枚举,完毕对内联事件及内联脚本的掣肘。

以堵住 a 标签内的 href="javascript:... 为例,大家得以这么写:

JavaScript

// 建立重点词黑名单 var keyword布莱克List = [ ‘xss’,
‘BAIDU_SSP__wrapper’, ‘BAIDU_DSPUI_FLOWBAR’ ];
document.add伊夫ntListener(‘click’, function(e) { var code = “”; // 扫描
<a href=”javascript:”> 的台本 if (elem.tagName == ‘A’ &&
elem.protocol == ‘javascript:’) { var code = elem.href.substr(11); if
(blackListMatch(keywordBlackList, code)) { // 注销代码 elem.href =
‘javascript:void(0)’; console.log(‘拦截疑心事件:’ + code); } } }, true);
/** * [黑名单匹配] * @param {[Array]} blackList [黑名单] *
@param {[String]} value [亟待证实的字符串] * @return {[Boolean]}
[false — 验证不经过,true — 验证通过] */ function
blackListMatch(blackList, value) { var length = blackList.length, i = 0;
for (; i < length; i++) { // 建立黑名单正则 var reg = new
RegExp(whiteList[i], ‘i’); // 存在黑名单中,拦截 if (reg.test(value))
{ return true; } } return false; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 建立关键词黑名单
var keywordBlackList = [
  ‘xss’,
  ‘BAIDU_SSP__wrapper’,
  ‘BAIDU_DSPUI_FLOWBAR’
];
  
document.addEventListener(‘click’, function(e) {
  var code = "";
  // 扫描 <a href="javascript:"> 的脚本
  if (elem.tagName == ‘A’ && elem.protocol == ‘javascript:’) {
    var code = elem.href.substr(11);
    if (blackListMatch(keywordBlackList, code)) {
      // 注销代码
      elem.href = ‘javascript:void(0)’;
      console.log(‘拦截可疑事件:’ + code);
    }
  }
}, true);
/**
* [黑名单匹配]
* @param  {[Array]} blackList [黑名单]
* @param  {[String]} value    [需要验证的字符串]
* @return {[Boolean]}         [false — 验证不通过,true — 验证通过]
*/
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;
  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], ‘i’);
    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

能够戳小编翻看DEMO。(打开页面后打开控制台查看
console.log)

点击图中那多少个按钮,能够见到如下:

美高梅开户网址 2

此处大家用到了黑名单匹配,下文还会细说。

 

页面被安置 iframe 中,重定向 iframe

先来说说我们的页面被内置了
iframe
的图景。也正是,互联网运维商为了尽恐怕地减小植入广告对原始网站页面包车型客车熏陶,日常会经过把原来网站页面放置到三个和原页面相同大小的
iframe 里面去,那么就足以由此这些 iframe
来隔开分离广告代码对原有页面包车型大巴熏陶。
美高梅开户网址 3

那种情况还相比好处理,大家只须求精通大家的页面是还是不是被嵌套在
iframe 中,假若是,则重定向外层页面到大家的平常页面即可。

这就是说有没有法子知情大家的页面当前留存于
iframe 中吗?有的,正是 window.self 与 window.top 。

4.2
面对错综复杂的政工供给,多次回调的node代码场景,你有如何看法?如何让代码更好读书和保证。

  1. 名字差异

静态脚本拦截

XSS 跨站脚本的精髓不在于“跨站”,在于“脚本”。

常见而言,攻击者或许运转商会向页面中流入二个<script>本子,具体操作都在本子中落到实处,那种威吓方式只供给注入3遍,有变动的话不供给每一回都重新注入。

我们只要未来页面上被注入了1个 <script src="http://attack.com/xss.js"> 脚本,大家的靶子正是阻止这些本子的执行。

听起来很狼狈啊,什么看头吧。就是在本子执行前发现这么些思疑脚本,并且销毁它使之不可能实施内部代码。

故而大家供给使用一些尖端 API ,能够在页面加载时对转移的节点进行检查和测试。

 

window.self

回来2个针对性当前
window 对象的引用。

 

最特出的是className,为了躲开JavaScript保留字,JS中跟class
attribute对应的property是className。

MutationObserver

MutationObserver 是 HTML5 新增的
API,效用很强劲,给开发者们提供了一种能在某些范围内的 DOM
树发生变化时作出确切反应的力量。

说的很玄妙,大致的情致便是力所能及监测到页面 DOM 树的变换,并作出反应。

MutationObserver() 该构造函数用来实例化二个新的Mutation观望者对象。

JavaScript

MutationObserver( function callback );

1
2
3
MutationObserver(
  function callback
);

目瞪狗呆,这第一次全国代表大会段又是啥?意思即是 MutationObserver
在观察时决不发现二个新因素就登时回调,而是将八个刻钟有个别里涌出的兼具因素,一起传过来。所以在回调中大家供给开始展览批量甩卖。而且,当中的 callback 会在钦定的
DOM
节点(目的节点)产生变化时被调用。在调用时,观望者对象会传给该函数多个参数,第二个参数是个包涵了若干个
MutationRecord 对象的数组,第二个参数则是以此旁观者对象自作者。

故而,使用 MutationObserver
,大家得以对页面加载的每种静态脚本文件,举行监察:

JavaScript

// MutationObserver 的不等包容性写法 var MutationObserver =
window.MutationObserver || window.WebKitMutationObserver ||
window.MozMutationObserver; // 该构造函数用来实例化2个新的 Mutation
观看者对象 // Mutation 观望者对象能监听在有些范围内的 DOM 树变化 var
observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) { // 重返被拉长的节点,也许为null.
var nodes = mutation.addedNodes; for (var i = 0; i < nodes.length;
i++) { var node = nodes[i]; if (/xss/i.test(node.src))) { try {
node.parentNode.removeChild(node); console.log(‘拦截可疑静态脚本:’,
node.src); } catch (e) {} } } }); }); // 传入指标节点和观察选项 // 假如target 为 document 或许 document.documentElement //
则当前文书档案中装有的节点添加与删除操作都会被考察到
observer.observe(document, { subtree: true, childList: true });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// MutationObserver 的不同兼容性写法
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver ||
window.MozMutationObserver;
// 该构造函数用来实例化一个新的 Mutation 观察者对象
// Mutation 观察者对象能监听在某个范围内的 DOM 树变化
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // 返回被添加的节点,或者为null.
    var nodes = mutation.addedNodes;
    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      if (/xss/i.test(node.src))) {
        try {
          node.parentNode.removeChild(node);
          console.log(‘拦截可疑静态脚本:’, node.src);
        } catch (e) {}
      }
    }
  });
});
// 传入目标节点和观察选项
// 如果 target 为 document 或者 document.documentElement
// 则当前文档中所有的节点添加与删除操作都会被观察到
observer.observe(document, {
  subtree: true,
  childList: true
});

能够旁观如下:能够戳我翻看DEMO。(打开页面后打开控制台查看
console.log)

美高梅开户网址 4

<script type="text/javascript" src="./xss/a.js"></script> 是页面加载一初始就存在的静态脚本(查看页面结构),大家应用
MutationObserver
能够在本子加载之后,执行此前这么些时间段对其剧情做正则匹配,发现恶意代码则 removeChild() 掉,使之无法推行。

window.top

回去窗口连串中的最顶层窗口的引用。

对于非同源的域名,iframe
子页面不可能透过 parent.location 或许 top.location
拿到现实的页面地址,可是能够写入 top.location
,也等于能够操纵父页面的跳转。

多少个属性分别可以又简写为 self 与 top,所以当发现大家的页面被嵌套在
iframe 时,能够重定向父级页面:

if (self != top) {
  // 我们的正常页面
  var url = location.href;
  // 父级页面重定向
  top.location = url;
}

  

⑤ 、你利用NodeJS编写了2个博客程序并把它配置到了一台linux服务器上,怎么样确定保证服务安全稳定性地可不止运转吧?(要求部分能够依附代码、命令等) 

<div class=”cls1 cls2″></div>

应用白名单对 src 实行匹配过滤

下边的代码中,我们看清2个js脚本是不是是恶意的,用的是这一句:

JavaScript

if (/xss/i.test(node.src)) {}

1
if (/xss/i.test(node.src)) {}

自然实际其中,注入恶意代码者不会那么傻,把名字改成 XSS
。所以,我们很有必不可少选拔白名单进行过滤和确立二个截留上报系统。

JavaScript

// 建立白名单 var whiteList = [ ‘www.aaa.com’, ‘res.bbb.com’ ]; /**
* [美高梅开户网址 ,白名单匹配] * @param {[Array]} whileList [白名单] * @param
{[String]} value [急需证实的字符串] * @return {[Boolean]} [false
— 验证不经过,true — 验证通过] */ function whileListMatch(whileList,
value) { var length = whileList.length, i = 0; for (; i < length;
i++) { // 建立白名单正则 var reg = new RegExp(whiteList[i], ‘i’); //
存在白名单中,放行 if (reg.test(value)) { return true; } } return false;
} // 只放行白名单 if (!whileListMatch(blackList, node.src)) {
node.parentNode.removeChild(node); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 建立白名单
var whiteList = [
  ‘www.aaa.com’,
  ‘res.bbb.com’
];
/**
* [白名单匹配]
* @param  {[Array]} whileList [白名单]
* @param  {[String]} value    [需要验证的字符串]
* @return {[Boolean]}         [false — 验证不通过,true — 验证通过]
*/
function whileListMatch(whileList, value) {
  var length = whileList.length,
    i = 0;
  for (; i < length; i++) {
    // 建立白名单正则
    var reg = new RegExp(whiteList[i], ‘i’);
    // 存在白名单中,放行
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}
// 只放行白名单
if (!whileListMatch(blackList, node.src)) {
  node.parentNode.removeChild(node);
}

此地咱们已经连续关乎白名单匹配了,下文还会用到,所以能够那里把它归纳封装成一个艺术调用。

运用白名单放行通常 iframe 嵌套

自然很多时候,或许运维供给,大家的页面会被以各类方法加大,也有大概是常规作业需求被嵌套在
iframe 中,那么些时候大家需求1个白名单只怕黑名单,当大家的页面被嵌套在
iframe 中且父级页面域名存在白名单中,则不做重定向操作。

上面也说了,使用
top.location.href 是不可能得到父级页面包车型客车 U大切诺基L
的,那时候,需求动用document.referrer

透过
document.referrer 能够获得跨域 iframe 父页面包车型地铁UMuranoL。

// 建立白名单
var whiteList = [
  'www.aaa.com',
  'res.bbb.com'
];

if (self != top) {
  var
    // 使用 document.referrer 可以拿到跨域 iframe 父页面的 URL
    parentUrl = document.referrer,
    length = whiteList.length,
    i = 0;

  for(; i<length; i++){
    // 建立白名单正则
    var reg = new RegExp(whiteList[i],'i');

    // 存在白名单中,放行
    if(reg.test(parentUrl)){
      return;
    }
  }

  // 我们的正常页面
  var url = location.href;
  // 父级页面重定向
  top.location = url;
}

 

前者安全,贰零壹肆Ali学校招聘前端在线测验标题。 

<script>

动态脚本拦截

上边使用 MutationObserver
拦截静态脚本,除了静态脚本,与之对应的就是动态变化的剧本。

JavaScript

var script = document.createElement(‘script’); script.type =
‘text/javascript’; script.src = ”;
document.getElementsByTagName(‘body’)[0].appendChild(script);

1
2
3
4
5
var script = document.createElement(‘script’);
script.type = ‘text/javascript’;
script.src = ‘http://www.example.com/xss/b.js’;
document.getElementsByTagName(‘body’)[0].appendChild(script);

要阻拦那类动态变化的本子,且拦截时机要在它插入 DOM
树中,执行在此以前,本来是足以监听 Mutation Events 中的 DOMNodeInserted 事件的。

更改 U劲客L 参数绕过运行商标记

诸如此类就完了啊?没有,我们固然重定向了父页面,可是在重定向的经过中,既然第②遍能够嵌套,那么那1次重定向的历程中页面可能又被
iframe 嵌套了,真尼玛蛋疼。

自然运转商那种劫持日常也是有迹可循,最健康的手段是在页面
U途乐L 中装置2个参数,例如
 ,其中 iframe_hijack_redirected=1 表示页面已经被威逼过了,就不再嵌套
iframe 了。所以依据那些特点,我们能够改写我们的 UXC90L
,使之看上去已经被勒迫了:

var flag = 'iframe_hijack_redirected';
// 当前页面存在于一个 iframe 中
// 此处需要建立一个白名单匹配规则,白名单默认放行
if (self != top) {
  var
    // 使用 document.referrer 可以拿到跨域 iframe 父页面的 URL
    parentUrl = document.referrer,
    length = whiteList.length,
    i = 0;

  for(; i<length; i++){
    // 建立白名单正则
    var reg = new RegExp(whiteList[i],'i');

    // 存在白名单中,放行
    if(reg.test(parentUrl)){
      return;
    }
  }

  var url = location.href;
  var parts = url.split('#');
  if (location.search) {
    parts[0] += '&' + flag + '=1';
  } else {
    parts[0] += '?' + flag + '=1';
  }
  try {
    console.log('页面被嵌入iframe中:', url);
    top.location.href = parts.join('#');
  } catch (e) {}
}

本来,假使那么些参数一改,防嵌套的代码就失效了。所以大家还索要建立3个禀报系统,当发现页面被嵌套时,发送2个阻拦上报,尽管重定向战败,也得以明白页面嵌入
iframe 中的 UEnclaveL,依据分析那些 U索罗德L
,不断增进大家的预防手段,这么些后文子禽提及。

六 、天猫首页需求完结那样二个效益,对于页面上非taobao.com域名下的链接,在用户点击时,必要在链接处弹出提示框,提示用户此链接非天猫域名下的链接,并授予用户选用是或不是继续访问。假若用户确认继续访问,则在新窗口打开链接。请写出相应的代码。 

var div = document.getElementByTagName(‘div’);

 

美高梅开户网址 5美高梅开户网址 6

div.className //cls1 cls2

Mutation Events 与 DOMNodeInserted

打开 MDN ,第三句就是:

该本性已经从 Web
标准中删去,就算有的浏览器方今还是支撑它,但或者会在今后的某些时间截至协理,请尽大概不要使用该特性。

即使不能够用,也能够精晓一下:

JavaScript

document.add伊夫ntListener(‘DOMNodeInserted’, function(e) { var node =
e.target; if (/xss/i.test(node.src) || /xss/i.test(node.innerHTML)) {
node.parentNode.removeChild(node); console.log(‘拦截狐疑动态脚本:’,
node); } }, true);

1
2
3
4
5
6
7
document.addEventListener(‘DOMNodeInserted’, function(e) {
  var node = e.target;
  if (/xss/i.test(node.src) || /xss/i.test(node.innerHTML)) {
    node.parentNode.removeChild(node);
    console.log(‘拦截可疑动态脚本:’, node);
  }
}, true);

然而可惜的是,使用方面包车型客车代码拦截动态变化的本子,能够阻碍到,然则代码也举办了:DOMNodeInserted 顾名思义,可以监听某个DOM 范围内的布局变化,与 MutationObserver 相比较,它的履行时机更早。

美高梅开户网址 7

但是 DOMNodeInserted 不再提议使用,所以监听动态脚本的职分也要提交 MutationObserver

心痛的是,在事实上履行进程中,使用 MutationObserver 的结果和 DOMNodeInserted 一样,能够监听拦截到动态脚本的浮动,可是力不从心在本子执行以前,使用 removeChild 将其移除,所以大家还亟需思考别的艺术。

内联事件及内联脚本拦截

在 XSS
中,其实能够注入脚本的格局要命的多,尤其是 HTML5
出来之后,一不留神,许多的新标签都得以用于注入可实施脚本。

列出一些比较广泛的流入方式:

  1. <a href="javascript:alert(1)" ></a>
  2. <iframe src="javascript:alert(1)" />
  3. <img src='x' onerror="alert(1)" />
  4. <video src='x' onerror="alert(1)" ></video>
  5. <div onclick="alert(1)" onmouseover="alert(2)" ><div>

除外一些未列出来的不行少见生僻的注入方式,大部分都是 javascript:... 及内联事件 on*

咱俩假设注入已经产生,那么有没有点子堵住这几个内联事件与内联脚本的执可以吗?

对此地点列出的
(1) (5)
,那种要求用户点击大概执行某种事件过后才实施的本子,大家是有点子实行防卫的。

<!DOCTYPE html>
<html>

</scrpit>

重写 setAttribute 与 document.write

浏览器事件模型

此处说能够阻挡,涉及到了事件模型连带的规律。

大家都明白,标准浏览器事件模型存在四个等级:

  • 破获阶段
  • 指标阶段
  • 冒泡阶段

对于三个那样 <a href="javascript:alert(222)" ></a> 的
a 标签而言,真正触发成分 alert(222) 是处于点击事件的靶子阶段。

点击上边的 click me ,先弹出
111 ,后弹出 222。

那正是说,大家只须求在点击事件模型的破获阶段对标签内 javascript:... 的剧情建立首要字黑名单,举行过滤审查,就能够形成我们想要的掣肘效果。

对于 on*
类内联事件也是同理,只是对于那类事件太多,大家不能够手动枚举,能够使用代码自动枚举,达成对内联事件及内联脚本的掣肘。

以阻挡 a
标签内的 href="javascript:... 为例,大家能够如此写:

// 建立关键词黑名单
var keywordBlackList = [
  'xss',
  'BAIDU_SSP__wrapper',
  'BAIDU_DSPUI_FLOWBAR'
];

document.addEventListener('click', function(e) {
  var code = "";

  // 扫描 <a href="javascript:"> 的脚本
  if (elem.tagName == 'A' && elem.protocol == 'javascript:') {
    var code = elem.href.substr(11);

    if (blackListMatch(keywordBlackList, code)) {
      // 注销代码
      elem.href = 'javascript:void(0)';
      console.log('拦截可疑事件:' + code);
    }
  }
}, true);

/**
 * [黑名单匹配]
 * @param  {[Array]} blackList [黑名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

能够戳我查看DEMO。(打开页面后打开控制台查看
console.log) 

点击图中那多少个按钮,能够看到如下:

美高梅开户网址 8

此处大家用到了黑名单匹配,下文还会细说。

 

<head>
    <meta charset=”utf-8″></meta>
    <title></title>
</head>

  1. 品种不等同

重写原生 Element.prototype.setAttribute 方法

在动态脚本插入执行前,监听 DOM 树的转变拦截它不行,脚本如故会执行。

这就是说大家必要向上查找,在剧本插入 DOM
树前的抓获它,那正是创办脚本时这么些空子。

假设将来有七个动态脚本是那般创造的:

JavaScript

var script = document.createElement(‘script’);
script.setAttribute(‘type’, ‘text/javascript’);
script.setAttribute(‘src’, ”);
document.getElementsByTagName(‘body’)[0].appendChild(script);

1
2
3
4
5
var script = document.createElement(‘script’);
script.setAttribute(‘type’, ‘text/javascript’);
script.setAttribute(‘src’, ‘http://www.example.com/xss/c.js’);
document.getElementsByTagName(‘body’)[0].appendChild(script);

而重写 Element.prototype.setAttribute 也是卓有功用的:大家发现此处运用了
setAttribute
方法,尽管咱们能够改写那几个原生方法,监听设置 src 属性时的值,通过黑名单恐怕白名单判断它,就足以判定该标签的合法性了。

JavaScript

// 保存原有接口 var old_setAttribute = Element.prototype.setAttribute;
// 重写 setAttribute 接口 Element.prototype.setAttribute =
function(name, value) { // 匹配到 <script src=’xxx’ > 类型 if
(this.tagName == ‘SCEnclaveIPT’ && /^src$/i.test(name)) { // 白名单匹配 if
(!whileListMatch(whiteList, value)) { console.log(‘拦截嫌疑模块:’,
value); return; } } // 调用原始接口 old_setAttribute.apply(this,
arguments); }; // 建立白名单 var whiteList = [ ‘www.yy.com’,
‘res.cont.yy.com’ ]; /** * [白名单匹配] * @param {[Array]}
whileList [白名单] * @param {[String]} value [供给注明的字符串]
* @return {[Boolean]} [false — 验证不通过,true — 验证通过] */
function whileListMatch(whileList, value) { var length =
whileList.length, i = 0; for (; i < length; i++) { // 建立白名单正则
var reg = new RegExp(whiteList[i], ‘i’); // 存在白名单中,放行 if
(reg.test(value)) { return true; } } return false; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 保存原有接口
var old_setAttribute = Element.prototype.setAttribute;
// 重写 setAttribute 接口
Element.prototype.setAttribute = function(name, value) {
  // 匹配到 <script src=’xxx’ > 类型
  if (this.tagName == ‘SCRIPT’ && /^src$/i.test(name)) {
    // 白名单匹配
    if (!whileListMatch(whiteList, value)) {
      console.log(‘拦截可疑模块:’, value);
      return;
    }
  }
  
  // 调用原始接口
  old_setAttribute.apply(this, arguments);
};
// 建立白名单
var whiteList = [
‘www.yy.com’,
‘res.cont.yy.com’
];
/**
* [白名单匹配]
* @param  {[Array]} whileList [白名单]
* @param  {[String]} value    [需要验证的字符串]
* @return {[Boolean]}         [false — 验证不通过,true — 验证通过]
*/
function whileListMatch(whileList, value) {
  var length = whileList.length,
    i = 0;
  for (; i < length; i++) {
    // 建立白名单正则
    var reg = new RegExp(whiteList[i], ‘i’);
    // 存在白名单中,放行
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

能够看到如下结果:能够戳笔者查看DEMO。(打开页面后打开控制台查看
console.log)

美高梅开户网址 9

重写 Element.prototype.setAttribute ,正是第叁保存原有接口,然后当有元素调用
setAttribute 时,检查传入的 src
是还是不是存在于白名单中,存在则放行,不设有则正是质疑成分,举办汇报并不予以实施。最终对放行的因素执行原生的 setAttribute ,也就是 old_setAttribute.apply(this, arguments);

上述的白名单匹配也得以换到黑名单匹配。

静态脚本拦截

XSS
跨站脚本的精华不在于“跨站”,在于“脚本”。

常备而言,攻击者也许运维商会向页面中注入3个<script>剧本,具体操作都在剧本中落实,那种威逼形式只要求注入一回,有转移的话不必要每一趟都重复注入。

咱俩只要现在页面上被注入了三个 <script src="http://attack.com/xss.js"> 脚本,我们的靶子正是拦住那么些剧本的推行。

听起来很不便啊,什么意思吧。便是在剧本执行前发现这一个狐疑脚本,并且销毁它使之不可能执行内部代码。

就此我们必要动用一些高档
API ,能够在页面加载时对转移的节点开始展览检查和测试。

 

<body>
    <a href=”;
    <a href=”;
    <a href=”;
    <script type=”text/javascript” src=”link.js”></script>
</body>

最特出的是style,不接受字符串型赋值。

MutationObserver

MutationObserver
是 HTML5 新增的 API,功能很强劲,给开发者们提供了一种能在某些范围内的
DOM 树爆发变化时作出确切反应的力量。

说的很神秘,差不离的意思就是能够监测到页面
DOM 树的转移,并作出反应。

MutationObserver() 该构造函数用来实例化多少个新的Mutation阅览者对象。

MutationObserver(
  function callback
);

目瞪狗呆,这一大段又是吗?意思就是MutationObserver
在察看时毫不发现2个新因素就应声回调,而是将多个时光部分里出现的有着因素,一起传过来。所以在回调中大家供给开始展览批量甩卖。而且,当中的 callback 会在钦命的
DOM
节点(指标节点)发生变化时被调用。在调用时,观看者对象会传给该函数四个参数,首个参数是个带有了好八个MutationRecord
对象的数组,第2个参数则是那么些观看者对象自作者。

为此,使用
MutationObserver
,大家得以对页面加载的各样静态脚本文件,进行监察:

// MutationObserver 的不同兼容性写法
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || 
window.MozMutationObserver;
// 该构造函数用来实例化一个新的 Mutation 观察者对象
// Mutation 观察者对象能监听在某个范围内的 DOM 树变化
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // 返回被添加的节点,或者为null.
    var nodes = mutation.addedNodes;

    for (var i = 0; i < nodes.length; i++) {
      var node = nodes[i];
      if (/xss/i.test(node.src))) {
        try {
          node.parentNode.removeChild(node);
          console.log('拦截可疑静态脚本:', node.src);
        } catch (e) {}
      }
    }
  });
});

// 传入目标节点和观察选项
// 如果 target 为 document 或者 document.documentElement
// 则当前文档中所有的节点添加与删除操作都会被观察到
observer.observe(document, {
  subtree: true,
  childList: true
});

能够看看如下:可以戳我翻看DEMO。(打开页面后打开控制台查看
console.log)

美高梅开户网址 10

<script type="text/javascript" src="./xss/a.js"></script> 是页面加载一始发就存在的静态脚本(查看页面结构),大家利用
MutationObserver
能够在本子加载之后,执行以前这几个时间段对其内容做正则匹配,发现恶意代码则 removeChild() 掉,使之不可能推行。

</html>

<div class=”cls1 cls2″ style=”color:blue” ></div>

重写嵌套 iframe 内的 Element.prototype.setAttribute

当然,上边的写法如若 old_setAttribute = Element.prototype.setAttribute 暴光给攻击者的话,直接选取old_setAttribute 就足以绕过大家重写的法门了,所以那段代码必须包在三个闭包内。

自然如此也不保障,虽可是今窗口下的 Element.prototype.setAttribute 已经被重写了。可是依然有手腕能够获得原生的 Element.prototype.setAttribute ,只须要一个新的
iframe 。

JavaScript

var newIframe = document.createElement(‘iframe’);
document.body.appendChild(newIframe); Element.prototype.setAttribute =
newIframe.contentWindow.Element.prototype.setAttribute;

1
2
3
4
var newIframe = document.createElement(‘iframe’);
document.body.appendChild(newIframe);
Element.prototype.setAttribute = newIframe.contentWindow.Element.prototype.setAttribute;

透过那个艺术,能够重复得到原生的 Element.prototype.setAttribute ,因为
iframe 内的条件和外围 window 是一心隔开的。wtf?

美高梅开户网址 11

咋办?大家见到创设 iframe
用到了 createElement,那么是还是不是足以重写原生 createElement 呢?但是除此之外createElement 还有 createElementNS ,还有可能是页面春天经存在
iframe,所以不适当。

这就在每当新创造3个新 iframe
时,对 setAttribute 进行珍视重写,这里又有用到 MutationObserver :

JavaScript

/** * 使用 MutationObserver 对转移的 iframe 页面举办监督, *
幸免调用内部原生 setAttribute 及 document.write * @return {[type]}
[description] */ function defenseIframe() { // 先尊崇当前页面
installHook(window); } /** * 完成单个 window 窗口的 setAttribute尊崇
* @param {[BOM]} window [浏览器window对象] * @return {[type]}
[description] */ function installHook(window) { // 重写单个 window
窗口的 setAttribute 属性 resetSetAttribute(window); // MutationObserver
的不一致包容性写法 var MutationObserver = window.MutationObserver ||
window.Web基特MutationObserver || window.MozMutationObserver; //
该构造函数用来实例化一个新的 Mutation 观看者对象 // Mutation
观看者对象能监听在某些范围内的 DOM 树变化 var observer = new
MutationObserver(function(mutations) {
mutations.forEach(function(mutation) { // 再次来到被加上的节点,可能为null.
var nodes = mutation.addedNodes; // 每一种遍历 for (var i = 0; i <
nodes.length; i++) { var node = nodes[i]; // 给生成的 iframe
里环境也装上海重型机器厂写的钩 if (node.tagName == ‘IFRAME’) {
installHook(node.contentWindow); } } }); }); observer.observe(document,
{ subtree: true, childList: true }); } /** * 重写单个 window 窗口的
setAttribute 属性 * @param {[BOM]} window [浏览器window对象] *
@return {[type]} [description] */ function
resetSetAttribute(window) { // 保存原有接口 var old_setAttribute =
window.Element.prototype.setAttribute; // 重写 setAttribute 接口
window.Element.prototype.setAttribute = function(name, value) { … }; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 使用 MutationObserver 对生成的 iframe 页面进行监控,
* 防止调用内部原生 setAttribute 及 document.write
* @return {[type]} [description]
*/
function defenseIframe() {
  // 先保护当前页面
  installHook(window);
}
/**
* 实现单个 window 窗口的 setAttribute保护
* @param  {[BOM]} window [浏览器window对象]
* @return {[type]}       [description]
*/
function installHook(window) {
  // 重写单个 window 窗口的 setAttribute 属性
  resetSetAttribute(window);
  // MutationObserver 的不同兼容性写法
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  // 该构造函数用来实例化一个新的 Mutation 观察者对象
  // Mutation 观察者对象能监听在某个范围内的 DOM 树变化
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      // 返回被添加的节点,或者为null.
      var nodes = mutation.addedNodes;
      // 逐个遍历
      for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i];
        // 给生成的 iframe 里环境也装上重写的钩子
        if (node.tagName == ‘IFRAME’) {
          installHook(node.contentWindow);
        }
      }
    });
  });
  observer.observe(document, {
    subtree: true,
    childList: true
  });
}
/**
* 重写单个 window 窗口的 setAttribute 属性
* @param  {[BOM]} window [浏览器window对象]
* @return {[type]} [description]
*/
function resetSetAttribute(window) {
  // 保存原有接口
  var old_setAttribute = window.Element.prototype.setAttribute;
  // 重写 setAttribute 接口
  window.Element.prototype.setAttribute = function(name, value) {
    …
  };
}

小编们定义了四个 installHook 方法,参数是3个 window ,在那些点子里,大家将重写传入的 window 下的
setAttribute
,并且安装3个 MutationObserver ,并对此窗口下以往只怕创制的 iframe 进行监听,尽管前景在此 window 下创造了3个iframe ,则对新的 iframe 也装上 installHook 方法,以此进行稀有保护。

 

View HTML Code

<script>

运用白名单对 src 进行匹配过滤

地方的代码中,大家看清三个js脚本是还是不是是恶意的,用的是这一句:

if (/xss/i.test(node.src)) {}

当然实际个中,注入恶意代码者不会那么傻,把名字改成
XSS
。所以,大家很有须要运用白名单实行过滤和树立三个阻挠上报系统。 

// 建立白名单
var whiteList = [
  'www.aaa.com',
  'res.bbb.com'
];

/**
 * [白名单匹配]
 * @param  {[Array]} whileList [白名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function whileListMatch(whileList, value) {
  var length = whileList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立白名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在白名单中,放行
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

// 只放行白名单
if (!whileListMatch(blackList, node.src)) {
  node.parentNode.removeChild(node);
} 

那边大家早就再三再四关乎白名单匹配了,下文还会用到,所以可以那里把它总结封装成两个办法调用。

美高梅开户网址 12美高梅开户网址 13

var div = document.getElementByTagName(‘div’);

重写 document.write

根据上述的章程,大家能够继承打通一下,还有如何方式能够重写,以便对页面实行更好的掩护。

document.write 是贰个很科学选用,注入攻击者,日常会接纳这些格局,往页面上注入一些弹窗广告。

我们能够重写 document.write ,使用重要词黑名单对剧情展开匹配。

什么样相比较符合当黑名单的要紧字呢?大家得以看看一些广告很多的页面:

美高梅开户网址 14

此地在页面最底部放置了1个 iframe ,里面装了广告代码,那里的最外层的 id
id="BAIDU_SSP__wrapper_u2444091_0" 就很吻合成为大家看清是或不是是恶意代码的几个标志,即便我们早已依据拦截上报收集到了一批黑名单列表:

JavaScript

// 建立正则拦截关键词 var keywordBlackList = [ ‘xss’,
‘BAIDU_SSP__wrapper’, ‘BAIDU_DSPUI_FLOWBAR’ ];

1
2
3
4
5
6
// 建立正则拦截关键词
var keywordBlackList = [
‘xss’,
‘BAIDU_SSP__wrapper’,
‘BAIDU_DSPUI_FLOWBAR’
];

接下去大家只必要利用这个重要字,对 document.write 传入的内容开始展览正则判断,就能分明是否要堵住document.write 那段代码。

JavaScript

“`javascript // 建立主要词黑名单 var keywordBlackList = [ ‘xss’,
‘BAIDU_SSP__wrapper’, ‘BAIDU_DSPUI_FLOWBAR’ ]; /** * 重写单个
window 窗口的 document.write 属性 * @param {[BOM]} window
[浏览器window对象] * @return {[type]} [description] */ function
resetDocumentWrite(window) { var old_write = window.document.write;
window.document.write = function(string) { if
(blackListMatch(keywordBlackList, string)) {
console.log(‘拦截猜疑模块:’, string); return; } // 调用原始接口
old_write.apply(document, arguments); } } /** * [黑名单匹配] *
@param {[Array]} blackList [黑名单] * @param {[String]} value
[亟待证实的字符串] * @return {[Boolean]} [false —
验证不经过,true — 验证通过] */ function blackListMatch(blackList,
value) { var length = blackList.length, i = 0; for (; i < length;
i++) { // 建立黑名单正则 var reg = new RegExp(whiteList[i], ‘i’); //
存在黑名单中,拦截 if (reg.test(value)) { return true; } } return false;
}<span style=”font-family: verdana, geneva;”> </span>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
“`javascript
// 建立关键词黑名单
var keywordBlackList = [
  ‘xss’,
  ‘BAIDU_SSP__wrapper’,
  ‘BAIDU_DSPUI_FLOWBAR’
];
/**
* 重写单个 window 窗口的 document.write 属性
* @param  {[BOM]} window [浏览器window对象]
* @return {[type]}       [description]
*/
function resetDocumentWrite(window) {
  var old_write = window.document.write;
  window.document.write = function(string) {
    if (blackListMatch(keywordBlackList, string)) {
      console.log(‘拦截可疑模块:’, string);
      return;
    }
    // 调用原始接口
    old_write.apply(document, arguments);
  }
}
/**
* [黑名单匹配]
* @param  {[Array]} blackList [黑名单]
* @param  {[String]} value    [需要验证的字符串]
* @return {[Boolean]}         [false — 验证不通过,true — 验证通过]
*/
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;
  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], ‘i’);
    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}<span style="font-family: verdana, geneva;"> </span>

大家得以把 resetDocumentWrite 放入上文的 installHook 方法中,就能对脚下
window 及拥有变化的 iframe 环境内的 document.write 实行重写了。

 

//Method 1 对事件进行处理。那样做的利益是防止了对每多少个a标签进行每个绑定,节省了岁月支出,其它轮代理公司码更简短。
(function() {
    var b = document.body;
    var reg = /^(https?:\/\/)?([\da-z\.-]+)\.\btaobao\b\.com([\/\w \.-]*)*\/?$/;

div.style // 对象

锁死 apply 和 call

接下去要介绍的那个是锁住原生的 Function.prototype.apply 和
Function.prototype.call 方法,锁住的情致正是使之不恐怕被重写。

此地要用到 Object.defineProperty ,用于锁死 apply 和 call。

动态脚本拦截

地点运用
MutationObserver
拦截静态脚本,除了静态脚本,与之相应的正是动态变化的本子。

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.example.com/xss/b.js';

document.getElementsByTagName('body')[0].appendChild(script); 

要阻止那类动态变化的脚本,且拦截时机要在它插入
DOM
树中,执行在此以前,本来是能够监听 Mutation Events 中的 DOMNodeInserted 事件的。

    function doClick(event) {
        if (event.target.tagName == ‘A’) {
            event.preventDefault();
            var href = event.target.href;
            if (reg.exec(href)) {
                location.href = href;
            } else {
                if (window.confirm(“非本地站点,是还是不是持续?”)) {
                    location.href = href;
                }
            }
        }
    }
    b.onclick = doClick;
})();

</scrpit>

 

//Method 2
(function() {
    var hrefs = document.getElementsByTagName(‘a’);
    for(var i=0; i <hrefs.length; i++) {
        var href = hrefs[i].getAttribute(“href”);
        hrefs[i].onclick = function(href) {
            return function() {
                var reg = /^(https?:\/\/)?([\da-z\.-]+)\.\btaobao\b\.com([\/\w \.-]*)*\/?$/;
                if(reg.exec(href)) {
                    return true;
                } else {
                    return window.confirm(“非本地站点,是或不是持续?”);
                }
            };
        }(href);
    }
})();

  1. 语义分歧等

Object.defineProperty

Object.defineProperty()
方法直接在一个指标上定义多个新属性,恐怕修改三个已经存在的性质,
并重临这么些指标。

JavaScript

Object.defineProperty(obj, prop, descriptor)

1
Object.defineProperty(obj, prop, descriptor)

其中:

  • obj – 需求定义属性的目的
  • prop – 需被定义或修改的属性名
  • descriptor – 需被定义或修改的属性的描述符

大家能够行使如下的代码,让 call 和 apply 不能被重写。

JavaScript

// 锁住 call Object.defineProperty(Function.prototype, ‘call’, { value:
Function.prototype.call, // 当且仅当仅当该属性的 writable 为 true
时,该属性才能被赋值运算符改变 writable: false, // 当且仅当该属性的
configurable 为 true 时,该属性才能够被更改,也能够被剔除 configurable:
false, enumerable: true }); // 锁住 apply
Object.defineProperty(Function.prototype, ‘apply’, { value:
Function.prototype.apply, writable: false, configurable: false,
enumerable: true });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 锁住 call
Object.defineProperty(Function.prototype, ‘call’, {
  value: Function.prototype.call,
  // 当且仅当仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变
  writable: false,
  // 当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除
  configurable: false,
  enumerable: true
});
// 锁住 apply
Object.defineProperty(Function.prototype, ‘apply’, {
  value: Function.prototype.apply,
  writable: false,
  configurable: false,
  enumerable: true
});

怎么要这么写吧?其实依然与上文的 重写 setAttribute 有关。

固然我们将原始 Element.prototype.setAttribute
保存在了2个闭包个中,不过还有奇技淫巧能够把它从闭包中给“偷出来”。

试一下:

JavaScript

(function() {})( // 保存原有接口 var old_setAttribute =
Element.prototype.setAttribute; // 重写 setAttribute 接口
Element.prototype.setAttribute = function(name, value) { // 具体细节 if
(this.tagName == ‘SC揽胜IPT’ && /^src$/i.test(name)) {} // 调用原始接口
old_setAttribute.apply(this, arguments); }; )(); // 重写 apply
Function.prototype.apply = function(){ console.log(this); } // 调用
setAttribute
document.getElementsByTagName(‘body’)[0].setAttribute(‘data-test’,’123′);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function() {})(
    // 保存原有接口
    var old_setAttribute = Element.prototype.setAttribute;
    // 重写 setAttribute 接口
    Element.prototype.setAttribute = function(name, value) {
        // 具体细节
        if (this.tagName == ‘SCRIPT’ && /^src$/i.test(name)) {}
        // 调用原始接口
        old_setAttribute.apply(this, arguments);
    };
)();
// 重写 apply
Function.prototype.apply = function(){
    console.log(this);
}
// 调用 setAttribute
document.getElementsByTagName(‘body’)[0].setAttribute(‘data-test’,’123′);

推断下面一段会输出什么?看看:
美高梅开户网址 15

居然重临了原生 setAttribute 方法!

那是因为咱们在重写 Element.prototype.setAttribute 时最终有 old_setAttribute.apply(this, arguments);这一句,使用到了
apply 方法,所以大家再重写 apply ,输出 this ,当调用被重写后的
setAttribute
就足以从中反向得到原生的被保存起来的 old_setAttribute 了。

诸如此类我们地点所做的嵌套 iframe 重写 setAttribute 就毫无意义了。

选择方面的 Object.defineProperty 能够锁死 apply 和 类似用法的 call
。使之不可能被重写,那么也就不可能从闭包中校大家的原生接口偷出来。这几个时候才算真正意义上的中标重写了我们想重写的属性。

Mutation Events 与 DOMNodeInserted

打开 MDN ,第叁句正是:

该性情已经从 Web
标准中删除,尽管部分浏览器方今依旧支撑它,但可能会在今后的某些时间甘休辅助,请尽大概不要采纳该性子。

虽说不能够用,也得以了然一下:

document.addEventListener('DOMNodeInserted', function(e) {
  var node = e.target;
  if (/xss/i.test(node.src) || /xss/i.test(node.innerHTML)) {
    node.parentNode.removeChild(node);
    console.log('拦截可疑动态脚本:', node);
  }
}, true);

然则可惜的是,使用方面包车型地铁代码拦截动态变化的脚本,可以阻挡到,然则代码也实践了:DOMNodeInserted 顾名思义,能够监听有些DOM
范围内的构造变迁,与 MutationObserver 相比,它的执行时机更早。

美高梅开户网址 16

但是 DOMNodeInserted 不再提出接纳,所以监听动态脚本的职务也要交给 MutationObserver

心痛的是,在事实上履行进程中,使用 MutationObserver 的结果和 DOMNodeInserted 一样,可以监听拦截到动态脚本的生成,不过力不从心在本子执行以前,使用 removeChild 将其移除,所以大家还索要思想别的措施。

View JavaScript Code

如a元素的href属性。

创造拦截上报

防守的手段也有局地了,接下去大家要树立贰个反映系统,替换上文中的
console.log() 日志。

举报系统有哪些用吗?因为大家用到了白名单,关键字黑名单,这几个数量都亟需不断的拉长,靠的便是汇报系统,将每一次拦截的信息传播服务器,不仅可以让大家程序员第近来间得知攻击的产生,更能够让我们不停采撷那类相关新闻以便更好的回复。

那里的以身作则笔者用 nodejs 搭多个尤其简练的服务器接受 http 上报请求。

先定义1个上报函数:

JavaScript

/** * 自定义上报 — 替换页面中的 console.log() * @param {[String]}
name [阻止类型] * @param {[String]} value [拦截值] */ function
hijackReport(name, value) { var img = document.createElement(‘img’),
hijackName = name, hijackValue = value.toString(), curDate = new
Date().getTime(); // 上报 img.src =
” + hijackName + ‘&value=’ +
hijackValue + ‘&time=’ + curDate;

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 自定义上报 — 替换页面中的 console.log()
* @param  {[String]} name  [拦截类型]
* @param  {[String]} value [拦截值]
*/
function hijackReport(name, value) {
  var img = document.createElement(‘img’),
    hijackName = name,
    hijackValue = value.toString(),
    curDate = new Date().getTime();
  // 上报
  img.src = ‘http://www.reportServer.com/report/?msg=’ + hijackName + ‘&value=’ + hijackValue + ‘&time=’ + curDate;

一经我们的服务器地址是 www.reportServer.com 这里,大家运用 img.src 发送三个http
请求到服务器http://www.reportServer.com/report/ ,每趟会带上大家自定义的掣肘类型,拦截内容以及反映时间。

用 Express 搭 nodejs 服务器并写一个大致的接收路由:

JavaScript

var express = require(‘express’); var app = express();
app.get(‘/report/’, function(req, res) { var queryMsg = req.query.msg,
queryValue = req.query.value, query提姆e = new
Date(parseInt(req.query.time)); if (queryMsg) { console.log(‘拦截类型:’

  • queryMsg); } if (queryValue) { console.log(‘拦截值:’ + queryValue); }
    if (query提姆e) { console.log(‘拦截时间:’ + req.query.time); } });
    app.listen(3002, function() { console.log(‘HttpHijack Server listening
    on port 3002!’); });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var express = require(‘express’);
var app = express();
app.get(‘/report/’, function(req, res) {
    var queryMsg = req.query.msg,
        queryValue = req.query.value,
        queryTime = new Date(parseInt(req.query.time));
    if (queryMsg) {
        console.log(‘拦截类型:’ + queryMsg);
    }
    if (queryValue) {
        console.log(‘拦截值:’ + queryValue);
    }
    if (queryTime) {
        console.log(‘拦截时间:’ + req.query.time);
    }
});
app.listen(3002, function() {
    console.log(‘HttpHijack Server listening on port 3002!’);
});

运行服务器,当有反映产生,我们将会收到到如下数据:

美高梅开户网址 17

好接下去正是多少入库,分析,添加黑名单,使用 nodejs 当然拦截爆发时发送邮件公告程序员等等,那么些就不再做展开。

 

 

<a href=”//m.taobao.com” ></div>

HTTPS 与 CSP

最终再不难谈谈 HTTPS 与
CSP。其实防御威逼最好的措施只怕从后端动手,前端能做的实在太少。而且由于源码的展露,攻击者很简单绕过大家的守卫手段。

重写 setAttribute 与 document.write

柒 、编写多个JavaScript函数,输入钦命项目标选拔器(仅需支撑id,class,tagName两种简单CSS采用器,无需同盟组合采用器)能够回到匹配的DOM节点,需考虑浏览器兼容性和品质。

<script>

CSP

CSP 即是 Content Security
Policy,翻译为情节安全策略。那几个专业与内容安全有关,首若是用来定义页面能够加载哪些财富,减少XSS 的发生。

MDN
– CSP

重写原生 Element.prototype.setAttribute 方法

在动态脚本插入执行前,监听
DOM 树的扭转拦截它不行,脚本照旧会执行。

那么我们需求向上查找,在本子插入
DOM 树前的破获它,那就是创办脚本时这些时机。

要是未来有三个动态脚本是那般创立的:

var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://www.example.com/xss/c.js');

document.getElementsByTagName('body')[0].appendChild(script);

而重写 Element.prototype.setAttribute 也是行得通的:大家发现此处运用了 setAttribute
方法,假设大家能够改写那些原生方法,监听设置 src 属性时的值,通过黑名单大概白名单判断它,就足以断定该标签的合法性了。

// 保存原有接口
var old_setAttribute = Element.prototype.setAttribute;

// 重写 setAttribute 接口
Element.prototype.setAttribute = function(name, value) {

  // 匹配到 <script src='xxx' > 类型
  if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {
    // 白名单匹配
    if (!whileListMatch(whiteList, value)) {
      console.log('拦截可疑模块:', value);
      return;
    }
  }

  // 调用原始接口
  old_setAttribute.apply(this, arguments);
};

// 建立白名单
var whiteList = [
'www.yy.com',
'res.cont.yy.com'
];

/**
 * [白名单匹配]
 * @param  {[Array]} whileList [白名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function whileListMatch(whileList, value) {
  var length = whileList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立白名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在白名单中,放行
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
}

可以观看如下结果:能够戳小编翻看DEMO。(打开页面后打开控制台查看
console.log)

美高梅开户网址 18

重写 Element.prototype.setAttribute ,正是率先保存原有接口,然后当有成分调用
setAttribute 时,检查传入的 src
是或不是留存于白名单中,存在则放行,不存在则正是狐疑成分,进行举报并不予以实施。最终对放行的因素执行原生的 setAttribute ,也就是 old_setAttribute.apply(this, arguments);

上述的白名单匹配也能够换到黑名单匹配。

/**

var a = document.getElementByTagName(‘a’);

HTTPS

可知实践 HTTP 威迫的根本原因,是 HTTP
协议没有办法对通讯对方的地位进行校验以及对数据完整性实行校验。假若能一蹴即至那个标题,则勒迫将无法自由发生。

HTTPS,是 HTTP over SSL 的意趣。SSL 协议是 Netscape 在 壹玖玖壹年第1遍建议的用于缓解传输层安全难点的互连网协议,其宗旨是遵照公钥密码学理论达成了对服务器身份注解、数据的私密性珍惜以及对数据完整性的校验等功用。

因为与本文主要内容关联性十分小,关于越多 CSP 和 HTTPS 的剧情能够活动谷歌(谷歌(Google))。

 

本文到此甘休,作者也是阅读前端安全那几个上边尽快,文章必然有所纰漏及错误,文章的格局也是过多看守措施中的一小部分,许多剧情参考上边文章,都以精品小说,相当班值日得一读:

  • 《web前端黑客技术揭秘》
  • XSS
    前端防火墙种类1~3
  • 【HTTP劫持和DNS劫持】实际JS对抗
  • 浅谈DNS劫持
  • HTTP Request
    Hijacking

 

动用 Javascript 写的三个防勒迫组件,已上流传 Github
– httphijack.js,欢迎感兴趣看看顺手点个
star ,本文示例代码,防备措施在组件源码中皆可找到。

除此以外组件处于测试修改阶段,未在生育条件使用,而且接纳了众多 HTML5
才支撑的 API,包容性是个难点,仅供就学调换。

到此本文结束,如若还有哪些难题依旧提议,能够多多沟通,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

打赏帮忙本人写出愈来愈多好文章,多谢!

打赏作者

 

* @param selector {String} 传入的CSS选择器。

a.href // “

打赏支持作者写出更多好小说,感激!

任选一种支付办法

美高梅开户网址 19
美高梅开户网址 20

2 赞 10 收藏 1
评论

重写嵌套 iframe 内的 Element.prototype.setAttribute

当然,下面的写法假设 old_setAttribute = Element.prototype.setAttribute 揭露给攻击者的话,直接运用old_setAttribute 就足以绕过大家重写的主意了,所以那段代码必须包在三个闭包内。

自然如此也不保障,即使眼下窗口下的 Element.prototype.setAttribute 已经被重写了。不过依旧有一手能够得到原生的 Element.prototype.setAttribute ,只须求1个新的
iframe 。

var newIframe = document.createElement('iframe');
document.body.appendChild(newIframe);

Element.prototype.setAttribute = newIframe.contentWindow.Element.prototype.setAttribute;

透过这几个点子,能够另行得到原生的 Element.prototype.setAttribute ,因为
iframe 内的环境和外围 window 是一点一滴切断的。wtf?

美高梅开户网址 21

怎么做?大家来看创建iframe
用到了 createElement,那么是不是能够重写原生 createElement 呢?可是除了createElement 还有 createElementNS ,还有大概是页面上早已存在
iframe,所以不适当。

那就在每当新成立3个新
iframe
时,对 setAttribute 实行维护重写,那里又有用到 MutationObserver :

/**
 * 使用 MutationObserver 对生成的 iframe 页面进行监控,
 * 防止调用内部原生 setAttribute 及 document.write
 * @return {[type]} [description]
 */
function defenseIframe() {
  // 先保护当前页面
  installHook(window);
}

/**
 * 实现单个 window 窗口的 setAttribute保护
 * @param  {[BOM]} window [浏览器window对象]
 * @return {[type]}       [description]
 */
function installHook(window) {
  // 重写单个 window 窗口的 setAttribute 属性
  resetSetAttribute(window);

  // MutationObserver 的不同兼容性写法
  var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

  // 该构造函数用来实例化一个新的 Mutation 观察者对象
  // Mutation 观察者对象能监听在某个范围内的 DOM 树变化
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      // 返回被添加的节点,或者为null.
      var nodes = mutation.addedNodes;

      // 逐个遍历
      for (var i = 0; i < nodes.length; i++) {
        var node = nodes[i];

        // 给生成的 iframe 里环境也装上重写的钩子
        if (node.tagName == 'IFRAME') {
          installHook(node.contentWindow);
        }
      }
    });
  });

  observer.observe(document, {
    subtree: true,
    childList: true
  });
}

/**
 * 重写单个 window 窗口的 setAttribute 属性
 * @param  {[BOM]} window [浏览器window对象]
 * @return {[type]} [description]
 */
function resetSetAttribute(window) {
  // 保存原有接口
  var old_setAttribute = window.Element.prototype.setAttribute;

  // 重写 setAttribute 接口
  window.Element.prototype.setAttribute = function(name, value) {
    ...
  };
} 

大家定义了1个 installHook 方法,参数是1个 window ,在那个主意里,大家将重写传入的 window 下的
setAttribute
,并且安装叁个 MutationObserver ,并对此窗口下以往只怕创设的 iframe 进行监听,尽管前景在此 window 下创设了一个iframe
,则对新的 iframe 也装上 installHook 方法,以此进行层层爱护。

* @return {Array}

a.getAttribute(‘href’) // “//m.taobao.com”,跟HTML代码中完全一致

有关笔者:chokcoco

美高梅开户网址 22

经不住小运似水,逃可是此间少年。

个人主页 ·
笔者的小说 ·
63 ·
   

美高梅开户网址 23

 

*/

</scrpit>

重写 document.write

依据上述的法子,大家得以继续挖掘一下,还有哪些艺术能够重写,以便对页面实行更好的维护。

document.write 是三个很科学选用,注入攻击者,平时会利用那些点子,往页面上注入一些弹窗广告。

作者们能够重写 document.write ,使用首要词黑名单对情节实行匹配。

怎样相比较符合当黑名单的根本字呢?大家得以看看一些广告很多的页面:

美高梅开户网址 24

此处在页面最底部放置了二个iframe ,里面装了广告代码,那里的最外层的 id
id="BAIDU_SSP__wrapper_u2444091_0" 就很吻合成为大家看清是或不是是恶意代码的3个标明,假使大家早已依据拦截上报收集到了一批黑名单列表:

// 建立正则拦截关键词
var keywordBlackList = [
'xss',
'BAIDU_SSP__wrapper',
'BAIDU_DSPUI_FLOWBAR'
];

接下去大家只要求选取那几个重庆大学字,对 document.write 传入的始末展开正则判断,就能分明是或不是要阻拦document.write 那段代码。 

```javascript
// 建立关键词黑名单
var keywordBlackList = [
  'xss',
  'BAIDU_SSP__wrapper',
  'BAIDU_DSPUI_FLOWBAR'
];

/**
 * 重写单个 window 窗口的 document.write 属性
 * @param  {[BOM]} window [浏览器window对象]
 * @return {[type]}       [description]
 */
function resetDocumentWrite(window) {
  var old_write = window.document.write;

  window.document.write = function(string) {
    if (blackListMatch(keywordBlackList, string)) {
      console.log('拦截可疑模块:', string);
      return;
    }

    // 调用原始接口
    old_write.apply(document, arguments);
  }
}

/**
 * [黑名单匹配]
 * @param  {[Array]} blackList [黑名单]
 * @param  {[String]} value    [需要验证的字符串]
 * @return {[Boolean]}         [false -- 验证不通过,true -- 验证通过]
 */
function blackListMatch(blackList, value) {
  var length = blackList.length,
    i = 0;

  for (; i < length; i++) {
    // 建立黑名单正则
    var reg = new RegExp(whiteList[i], 'i');

    // 存在黑名单中,拦截
    if (reg.test(value)) {
      return true;
    }
  }
  return false;
} 

我们得以把 resetDocumentWrite 放入上文的 installHook 方法中,就能对眼前window 及拥有变更的 iframe 环境内的 document.write 实行重写了。

var query = function(selector){

  1. 另一方面同步关系

 

//再次回到查找到的节点数组

value是2个极为尤其的attribute/property。

锁死 apply 和 call

接下去要介绍的那些是锁住原生的
Function.prototype.apply 和 Function.prototype.call
方法,锁住的情趣正是使之不只怕被重写。

此处要用到 Object.defineProperty ,用于锁死
apply 和 call。

return [];

<input value = “cute” />

 

}

<script>

Object.defineProperty

Object.defineProperty()
方法直接在二个对象上定义贰个新属性,或许涂改一个早已存在的性质,
并重返那么些目的。

Object.defineProperty(obj, prop, descriptor)

其中: 

  • obj –
    须要定义属性的目的
  • prop –
    需被定义或改动的属性名
  • descriptor –
    需被定义或修改的质量的讲述符

大家得以行使如下的代码,让
call 和 apply 不恐怕被重写。

// 锁住 call
Object.defineProperty(Function.prototype, 'call', {
  value: Function.prototype.call,
  // 当且仅当仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变
  writable: false,
  // 当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除 
  configurable: false,
  enumerable: true
});
// 锁住 apply
Object.defineProperty(Function.prototype, 'apply', {
  value: Function.prototype.apply,
  writable: false,
  configurable: false,
  enumerable: true
}); 

为啥要这么写吧?其实还是与上文的 重写 setAttribute 有关。

就算我们将原始
Element.prototype.setAttribute
保存在了八个闭包在那之中,不过还有奇技淫巧能够把它从闭包中给“偷出来”。

试一下:

(function() {})(
    // 保存原有接口
    var old_setAttribute = Element.prototype.setAttribute;
    // 重写 setAttribute 接口
    Element.prototype.setAttribute = function(name, value) {
        // 具体细节
        if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {}
        // 调用原始接口
        old_setAttribute.apply(this, arguments);
    };
)();
// 重写 apply
Function.prototype.apply = function(){
    console.log(this);
}
// 调用 setAttribute
document.getElementsByTagName('body')[0].setAttribute('data-test','123'); 

推断上边一段会输出什么?看看:
美高梅开户网址 25

竟然再次回到了原生
setAttribute 方法!

那是因为我们在重写 Element.prototype.setAttribute 时最终有 old_setAttribute.apply(this, arguments);这一句,使用到了
apply 方法,所以大家再重写 apply ,输出 this ,当调用被重写后的
setAttribute
就能够从中反向得到原生的被保存起来的 old_setAttribute 了。

那样大家地点所做的嵌套
iframe 重写 setAttribute 就毫无意义了。

选用方面的 Object.defineProperty 能够锁死
apply 和 类似用法的 call
。使之不能被重写,那么也就不能够从闭包上将大家的原生接口偷出来。那一个时候才算真的含义上的功成名就重写了大家想重写的属性。

美高梅开户网址 26美高梅开户网址 27

var input = document.getElementByTagName(‘input’);

 

<!DOCTYPE html>
<html>

//若property没有安装,则结果是attribute

建立拦截上报

防守的手腕也有局地了,接下去大家要树立3个报告系统,替换上文中的
console.log() 日志。

申报系统有哪些用吧?因为我们用到了白名单,关键字黑名单,那些数量都必要不停的拉长,靠的正是汇报系统,将每便拦截的新闻传播服务器,不仅能够让大家程序员第一时间得知攻击的发生,更能够让咱们不住采撷那类相关音信以便更好的应对。

此间的示范笔者用 nodejs 搭多少个格外简约的服务器接受
http 上报请求。

先定义贰个举报函数:

/**
 * 自定义上报 -- 替换页面中的 console.log()
 * @param  {[String]} name  [拦截类型]
 * @param  {[String]} value [拦截值]
 */
function hijackReport(name, value) {
  var img = document.createElement('img'),
    hijackName = name,
    hijackValue = value.toString(),
    curDate = new Date().getTime();

  // 上报
  img.src = 'http://www.reportServer.com/report/?msg=' + hijackName + '&value=' + hijackValue + '&time=' + curDate;
}

一旦大家的服务器地址是 www.reportServer.com 那里,大家运用 img.src 发送一个http
请求到服务器http://www.reportServer.com/report/ ,每一遍会带上大家自定义的阻拦类型,拦截内容以及反映时间。

用 Express
搭 nodejs 服务器并写一个差不离的接收路由:

var express = require('express');
var app = express();

app.get('/report/', function(req, res) {
    var queryMsg = req.query.msg,
        queryValue = req.query.value,
        queryTime = new Date(parseInt(req.query.time));

    if (queryMsg) {
        console.log('拦截类型:' + queryMsg);
    }

    if (queryValue) {
        console.log('拦截值:' + queryValue);
    }

    if (queryTime) {
        console.log('拦截时间:' + req.query.time);
    }
});

app.listen(3002, function() {
    console.log('HttpHijack Server listening on port 3002!');
});

运营服务器,当有反映爆发,大家将会接到到如下数据:

美高梅开户网址 28

好接下去就是多少入库,分析,添加黑名单,使用 nodejs 当然拦截产生时发送邮件公告程序员等等,这几个就不再做展开。

<head>
    <title></title>
</head>

input.value //cute

 

<body>
    <div class=”test” name=”a”></div>
    <div class=”test” name=”b”></div>
    <div class=”test” name=”c”></div>
    <div class=”test” name=”d”></div>
    <div class=”test” name=”e”></div>

input.getAttribute(‘value’); //cute

HTTPS 与 CSP

说到底再简单谈谈
HTTPS 与
CSP。其实防御威吓最好的格局或然从后端动手,前端能做的实在太少。而且由于源码的展露,攻击者很不难绕过大家的防卫手段。

   <script type=”text/javascript”>
        var query = function(selector) {
            var reg_id = /^#[\w]+/;
            var reg_class = /^\.[\w]+/;

 

CSP

CSP 便是Content Security
Policy,翻译为剧情安全策略。这么些标准与内容安全有关,首假设用来定义页面能够加载哪些能源,减少XSS 的发生。

MDN
– CSP

            var elems;
            if (document.querySelectorAll) {
                return document.querySelectorAll(selector);
            }

input.value = ‘hello’;

HTTPS

可见履行
HTTP 威胁的根本原因,是 HTTP
协议没有艺术对通讯对方的身份展开校验以及对数据完整性举行校验。假诺能化解这几个难题,则威胁将不能轻易产生。

HTTPS,是
HTTP over SSL 的意思。SSL 协议是 Netscape 在 1993年第三回提出的用于消除传输层安全难点的互联网协议,其基本是依据公钥密码学理论达成了对服务器身份认证、数据的私密性爱慕以及对数据完整性的校验等职能。

因为与本文重要内容关联性不大,关于越多CSP 和 HTTPS 的内容能够活动谷歌。

 

正文到此截止,作者也是阅读前端安全这一个方面尽快,文章必然有所纰漏及错误,小说的章程也是累累守护措施中的一小部分,许多剧情参考上边作品,都以精品小说,分外值得一读:

  • 《web前端黑客技术揭秘》
  • XSS
    前端防火墙类别1~3
  • 【HTTP劫持和DNS劫持】实际JS对抗
  • 浅谈DNS劫持
  • HTTP
    Request
    Hijacking

 

行使
Javascript 写的三个防威胁组件,已上传出 Github
– httphijack.js,欢迎感兴趣看看顺手点个
star ,本文示例代码,防范方法在组件源码中皆可找到。

其它组件处于测试修改阶段,未在生养条件使用,而且使用了众多
HTML5 才支撑的 API,包容性是个难题,仅供就学沟通。

到此本文截至,假诺还有哪些难题依然建议,能够多多交换,原创小说,文笔有限,才疏学浅,文中若有不正之处,万望告知。

            if (reg_id.test(selector)) {
                elems = document.getElementById(selector.slice(1));
            } else if (reg_class.test(selector)) {
                elems = getElementsByClassName(selector.slice(1));
            } else {
                elems = document.getElementsByTagName(selector);
            }

//若value属性已经设置,则attribute不变,property变化,成分上其实的功能是property优先

            return elems;
        };

input.value //hello

        function getElementsByClassName(className) {
            var arr = document.getElementsByTagName(“*”),
                result;
            for (var i = 0, len = arr.length; i < len; i++) {
                if (arr[i].className.indexOf(className) != -1) {
                    result.push(arr[i]);
                }
            }
            return result;
        }

input.getAttribute(‘value’); //cute

        console.log(query(“.test”));
</script>

</scrpit>

</body>

除却,checkbox的突显状态由checked和indeterminate三个property决定,而唯有3个名为checked的property,那种情况下property是更全面包车型地铁拜会模型。

</html>

③ 、特殊境况

View Code

1.mutation

美高梅开户网址 29美高梅开户网址 30

采纳mutation observer,只可以监测到attribute变化。

var query = function(selector) {
        var rId = /^#/;
        var rCls = /^\./;
        //标准浏览器
        if (window.addEventListener) {
            return document.querySelectorAll(selector);
        }
        //IE
        if (rId.test(selector)) {
            return document.getElementById(selector.slice(1));
        }
        if (rCls.test(selector)) {
            return getElementsByClass(selector.slice(1));
        }
        return document.getELementsByTagName(selector);

var observer = new MutationObserver(function(mutations){

    };

for(var i = 0; i < mutations.length; i++) {

    var getElementsByClass = function(searchClass, node, tag) {
        var classElements = new Array();
        if (node == null)
            node = document;
        if (tag == null)
            tag = ‘*’;
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;
        var pattern = new RegExp(“(^|\\s)” + searchClass + “(\\s|$)”);
        for (i = 0, j = 0; i < elsLen; i++) {
            if (pattern.test(els[i].className)) {
                console.log(true);classElements[j] = els[i];
                j++;
            }
        }
        return classElements;
    }; 

var mutation = mutations[i];

View Code

console.log(mutation.attributeName);

⑧ 、3个页面上有大批量的图样,加载非常慢,你有何样方法优化那些图片的加载,给用户更好的经验。 

}

a. CSS
Coca Colas:将四个网页中提到的零碎图片,整合到一张大图中,然后利用CSS技术显示出来。这样一来,裁减了整整页面图片的深浅,并且能压缩网页http请求次数,从而大大地拉长网页的属性。

});

b. 压缩图片

observer.observe(element,{attributes:true});

c. 成效图片优先加载

element.prop1 = ‘aa’ // 不会接触

d.
图片格式优化(JPEG,GIF,和PNG):对于产品图片品质须求极高,使用JPEG格式,用GIF做动画或是装饰性小图,PNG同时也善于处理大约地装饰图而只需极小的容量

element.setAttribute(‘attr1’, ‘aa’) //会触发

 

2.custom element

⑨ 、使用 JavaScript 的 Promise
方式实现延迟3秒输出 

在利用WebComponents时,能够定义attribute和property,两者能够并行反射,也得以全无涉及。

美高梅开户网址 31美高梅开户网址 32

var MyElementProto = Object.create(HTMLElement.prototype, {

// 先封装1个回到promise的函数
var Promise = function () {
   
};

createdCallback : {

Promise.prototype.then = function (onResolved, onRejected) {
    this.onResolved = onResolved;
    this.onRejected = onRejected;
    return this;
};

value : function() { }

Promise.prototype.resolve = function (value) {
   this.onResolved(value);
   return this;
};
 
Promise.prototype.reject = function (error) {
    this.onRejected(error);
    return this;
};

}

new Promise().then(function(value) {
    setTimeout(function() {
        console.log(value);
    }, 3000);
}, function(error) {
    alert(“error”);
}).resolve(“3 sec output.”);

});

View Code

 

⑩ 、完结三个页面

//定义property

美高梅开户网址 33美高梅开户网址 34

Object.defineProperty(MyElementProto,’prop1′, {

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
</head>
<body style=”width:960px; margin: 20px auto;”>
    <div id=”nav” style=”height: 80px;text-align: center;font-size: 3em;border-bottom: solid #DDDDDD;”>阿里Baba(Alibaba)面试题</div>
    <div id = “Content”>
        <div id = “sidebar” style=”width:20%; float: left;”>
            <ul>
                <li style=”list-style: none;”><a href=”” style=”text-decoration: none;”>前端工程师面试题</a></li>
                <li style=”list-style: none;”><a href=”” style=”text-decoration: none;”>设计师面试题</a></li>
                <li style=”list-style: none;”><a href=”” style=”text-decoration: none;”>java面试题</a></li>
            </ul>    
        </div>
        <div id = “main” style=”width:80%;”>
            <table style=”border-collapse:collapse;”>
                <tr> 
                    <th  style=”border:1px solid black; width:250px; height:40px;”>小编是标题一</th>
                    <th  style=”border:1px solid black; width:90px; height:40px;”>标题二</th>
                    <th  style=”border:1px solid black; width:90px; height:40px;”>三</th>
                </tr>
                <t>
                    <td style=”border:1px solid black; width:250px; height:40px;”>内容</td>
                    <td style=”border:1px solid black; width:250px; height:40px;”>内容</td>
                    <td style=”border:1px solid black; width:250px; height:40px;”>内容</td>
                </tr>
            </table>
        </div>
    </div>
</body>
</html>

get:function(){

View HTML Code

return //

 

},

11、个人的github地址 

set:function(){

console.log(‘property change’);//do something

}

});

 

//定义attribute

MyElementProto.attributeChangedCallback = function(attr, oldVal, newVal)
{

if(attr === ‘attr1’) {

console.log(‘attribute change’);//do something

}

};

 

window.MyElement = document.registerElement(‘my-element’, {

prototype: MyElementProto

});

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图