转载请注明出处:unclekeith: 前端安全之XSSjavascript
XSS, 即为(Cross Site Scripting), 中文名为跨站脚本, 是发生在目标用户的浏览器层面上的,当渲染DOM树的过程成发生了不在预期内执行的JS代码时,就发生了XSS攻击。css
跨站脚本的重点不在‘跨站’上,而在于‘脚本’上。大多数XSS攻击的主要方式是嵌入一段远程或者第三方域上的JS代码。其实是在目标网站的做用域下执行了这段js代码。html
反射型 XSS前端
反射型XSS,也叫非持久型XSS,是指发生请求时,XSS代码出如今请求URL中,做为参数提交到服务器,服务器解析并响应。响应结果中包含XSS代码,最后浏览器解析并执行。java
从概念上能够看出,反射型XSS代码是首先出如今URL中的,而后须要服务端解析,最后须要浏览器解析以后XSS代码才可以攻击。node
举一个小栗子。git
使用express起一个web服务器,而后设置一下请求接口。经过ajax的GET请求将参数发往服务器,服务器解析成json后响应。将返回的数据解析后显示到页面上。(没有对返回的数据进行解码和过滤等操做。)github
html <textarea name="txt" id="txt" cols="80" rows="10"> <button type="button" id="test">测试</button> js var test = document.querySelector('#test') test.addEventListener('click', function () { var url = `/test?test=${txt.value}` // 1. 发送一个GET请求 var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { // 3. 客户端解析JSON,并执行 var str = JSON.parse(xhr.responseText).test var node = `${str}` document.body.insertAdjacentHTML('beforeend', node) } else { console.log('error', xhr.responseText) } } } xhr.open('GET', url, true) xhr.send(null) }, false) express var express = require('express'); var router = express.Router(); router.get('/test', function (req, res, next) { // 2. 服务端解析成JSON后响应 res.json({ test: req.query.test }) })
如今咱们经过给textarea添加一段有攻击目的的img标签,web
<img src="null" onerror='alert(document.cookie)' />
实际的页面时这样的。
ok如今,咱们点击 <测试> 按钮,一个XSS攻击就发生了。下面图片中是获取了本地的部分cookie信息
实际上,咱们只是模拟攻击,经过alert获取到了我的的cookie信息。可是若是是黑客的话,他们会注入一段第三方的js代码,而后将获取到的cookie信息存到他们的服务器上。这样的话黑客们就有机会拿到咱们的身份认证作一些违法的事情了。
ajax
以上,存在的一些问题,主要在于没有对用户输入的信息进行过滤,同时没有剔除掉DOM节点中存在的一些有危害的事件和一些有危害的DOM节点。
存储型 XSS
存储型XSS,也叫持久型XSS,主要是将XSS代码发送到服务器(不论是数据库、内存仍是文件系统等。),而后在下次请求页面的时候就不用带上XSS代码了。
最典型的就是留言板XSS。用户提交了一条包含XSS代码的留言到数据库。当目标用户查询留言时,那些留言的内容会从服务器解析以后加载出来。浏览器发现有XSS代码,就当作正常的HTML和JS解析执行。XSS攻击就发生了。
DOM XSS
DOM XSS攻击不一样于反射型XSS和存储型XSS,DOM XSS代码不须要服务器端的解析响应的直接参与,而是经过浏览器端的DOM解析。这彻底是客户端的事情。
DOM XSS代码的攻击发生的可能在于咱们编写JS代码形成的。咱们知道eval语句有一个做用是将一段字符串转换为真正的JS语句,所以在JS中使用eval是很危险的事情,容易形成XSS攻击。避免使用eval语句。
如如下代码
test.addEventListener('click', function () { var node = window.eval(txt.value) window.alert(node) }, false) txt中的代码以下 <img src='null' onerror='alert(123)' />
以上经过eval语句就形成了XSS攻击。
从以上的反射型和DOM XSS攻击能够看出,咱们不能原样的将用户输入的数据直接存到服务器,须要对数据进行一些处理。以上的代码出现的一些问题以下
若是将以上三点都在渲染过程当中过滤,那么出现的XSS攻击的几率也就小不少。
解决方法以下
对cookie的保护
document.cookie
读取cookie。服务端能够设置此字段。对用户输入数据的处理
经过一个例子讲解一下如何处理用户输入的数据。
实现原理以下:
<script src='/javascripts/htmlparse.js'></script> <script src='/javascripts/he.js'></script> // 第三方库资源在文章底部给出 // parse函数实现以下 function parse (str) { // str假如为某个DOM字符串 // 1. result为处理以后的DOM节点 let result = '' // 2. 解码 let decode = he.unescape(str, { strict: true }) HTMLParser(decode, { start (tag, attrs, unary) { // 3. 过滤常见危险的标签 if (tag === 'script' || tag === 'img' || tag === 'link' || tag === 'style' || tag === 'iframe' || tag === 'frame') return result += `<${tag}` for (let i = 0; i < attrs.length; i++) { let name = (attrs[i].name).toLowerCase() let value = attrs[i].escaped // 3. 过滤掉危险的style属性和js事件 if (name === 'style' || name === 'href' || name === 'src' || ~name.indexOf('on')) continue result += ` ${name}=${value}` } result += `${unary ? ' /' : ''} >` }, chars (text) { result += text }, comment (text) { result += `<!-- ${text} -->` }, end (tag) { result += `</${tag}>` } }) return result }
所以,有了以上的parse函数以后,就能够避免大部分的xss攻击了。
test.addEventListener('click', function () { // ... 省略部分代码 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { // 3. 客户端解析JSON,并执行 // test按钮的点击事件中惟一的变化就是使用parse对服务端返回的数据进行了解码和过滤的处理。 var str = parse(JSON.parse(xhr.responseText).test) // 经过parse解析以后返回的数据就是安全的DOM字符串 var node = `${str}` document.body.insertAdjacentHTML('beforeend', node) } } } // ... 省略部分代码 }, false)
那么,栗子说完了。
稍微总结一下
参考资源