关于CSRF攻击可以看这篇文章。关于CSRF攻击的原理以及防御措施
跨站脚本攻击
跨站脚本攻击(Cross Site Scripting):攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息等,进而危害数据安全。
对用户造成的危害如:
- 窃取Cookie
- 监听用户行为,比如输入账号密码后直接发送到黑客服务器
- 修改 DOM 伪造登录表单
- 在页面中生成浮窗广告
XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
XSS的类型
- 存储型XSS:也叫“持久型”,攻击者将恶意代码提交到目标网站的数据库中,用户打开网站是,网站服务端将恶意代码从数据库中取出,拼接在HTML中返回浏览器,之后用户浏览器收到响应后解析执行混入其中的恶意代码,恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户行为,调用目标网站接口执行攻击者指定的操作。
- 论坛发帖
- 评论
- 用户私信
- 反射型XSS:也叫“非持久型”,攻击者事先制作好攻击链接,需要诱导欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。
用户点击攻击链接将一段含有恶意代码的请求提交给 Web 服务器,Web 服务器接收到请求时,又将恶意代码反射给了浏览器端。用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。- 常见于通过 URL 传递参数的功能,如网站搜索、跳转等。(由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。)
- DOM型:前端 JavaScript 代码不够严谨,把不可信的内容插入到了页面。
举个例子:
<script>
//该函数接受一个用户提供的 URL 并展示在页面
function displayURL(url) {
// URL 通过 innerHTML 获得,执行 JavaScript 代码
document.getElementById('display').innerHTML = url;
}
</script>
<!-- 用户的输入传递给 displayURL 函数 -->
<p>Enter a URL to display: <input type="text" id="user-input" /></p>
<button onclick="displayURL(document.getElementById('user-input').value)">
Display URL
</button>
<!-- URL 在 div 中显示 -->
<div id="display"></div>
XSS防御的措施
-
对用户的输入数据进行过滤,编码等操作。
-
对cookie进行保护,设置为httpOnly,防止客户端通过
document.cookie
读取cookie(服务端设置)。 -
利用 CSP(内容安全策略)
这里以设置 HTTP Header 来举例:
只允许加载本站资源
Content-Security-Policy: default-src 'self'
只允许加载 HTTPS 协议图片
Content-Security-Policy: img-src https://*
通过CSP可以:- 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
- 禁止向第三方域提交数据,这样用户数据也不会外泄;
- 禁止执行内联脚本和未授权的脚本
-
预防存储型和反射型XSS攻击
- 改成纯前端渲染,把代码和数据分隔开
- 对HTML做充分转义
vue中预防xss
- 输入过滤和验证
<template>
<div>
<input v-model="inputValue" v-filter>
</div>
</template>
Vue.directive('filter', {
bind: function(el, binding, vnode) {
el.addEventListener('input', function() {
let value = el.value;
value = value.replace(/<[^>]*>|[\r\n\t]/gi, '');
value = value.replace(/[&<>"]/gi, function(match) {
switch (match) {
case '&':
return '&';
case '<':
return '<';
case '>':
return '>';
case '\"':
return '"';
}
});
vnode.context[binding.expression] = value;
});
}
});
- 输出转义
<template>
<div v-html="htmlString"></div>
</template>
export default {
data() {
return {
htmlString: '<div>Hello & World!</div>'
};
}
}
- CSP(Content Security Policy)
用vue-meta库来添加CSP策略,限制网页中可以加载的内容和脚本
设置不同的源策略(如defaultSrc、scriptSrc、styleSrc等)来限制不同类型的资源的加载,如下设置了只允许加载本地资源和内联js脚本,禁止加载其他域名的脚本和资源。
import Vue from 'vue';
import VueMeta from 'vue-meta';
Vue.use(VueMeta, {
keyName: 'metaInfo',
attribute: 'data-vue-meta',
ssrAttribute: 'data-vue-meta-server-rendered',
tagIDKeyName: 'vmid',
refreshOnceOnNavigation: true,
contentSecurityPolicy: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"]
}
});
- HTTP-only Cookie
在Vue.js中可以通过在服务器端设置HTTP-only标记来防止XSS攻击。当HTTP-only标记被设置时,浏览器只会在HTTP请求中发送cookie信息,而禁止使用JavaScript等脚本来读取或修改cookie。例如,在使用Express.js作为服务器端框架时,可以使用以下代码来设置HTTP-only标记:
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
app.get('/', function(req, res) {
res.cookie('sessionId', '12345', { httpOnly: true });
res.send('Hello World!');
});
- Sanitize HTML
在Vue.js中可以使用DOMPurify库来过滤和清理用户输入的HTML代码,从而减少XSS攻击的风险。DOMPurify库可以检测和清理HTML代码中的恶意代码,包括JavaScript脚本、HTML注入、CSS注入、URL跳转等攻击方式。例如,在Vue.js中可以使用以下代码来过滤用户输入的HTML代码:
import DOMPurify from 'dompurify';
const dirtyHtml = '<script>alert("XSS Attack!");</script><a href="http://example.com">Link</a>';
const cleanHtml = DOMPurify.sanitize(dirtyHtml);
console.log(cleanHtml); // <a href="http://example.com">Link</a>
- Escape Output
使用{{}}语法来显示动态内容,需要注意的是,需要对显示的内容进行转义
在下面代码中,使用escape方法来转义输出的内容,使用正则表达式来匹配需要转义的字符,并使用替换函数来替换字符,从而实现转义输出的功能。
<template>
<div>
{{ escape(content) }}
</div>
</template>
<script>
export default {
data() {
return {
content: '<script>alert("XSS Attack!");</script>'
}
},
methods: {
escape(content) {
return content.replace(/[<>"&]/g, function(match) {
return {
'<': '<',
'>': '>',
'"': '"',
'&': '&'
}[match];
});
}
}
}
</script>