面试宝典-前端项目如何防范CSRF攻击
前端面试中,经常会被问到的知识点之一,什么是XSS攻击?
XSS攻击比起CSRF攻击更加需要前端开发者关注,尤其是在一些富文本输入及处理的情况之下。
什么是XSS攻击?
表现
首先什么是xss攻击,官方化的解释是: XSS 攻击,即跨站脚本攻击(Cross-Site Scripting) ,是一种常见的 Web 应用安全漏洞。由于其缩写 CSS 容易与层叠样式表(Cascading Style Sheets)的缩写混淆,所以通常简称为 XSS。
通俗的讲就是,一些别有用心的人,搞了一段不是你项目中的可执行的js代码插入到你的页面里面,并且被成功执行了,这个危害就取决于他这段代码做了些什么事情了。
举个例子: 现有服务端代码:
const express = require('express');
const app = express();
// 处理搜索请求
app.get('/search', (req, res) => {
const keyword = req.query.keyword || '';
// 未对用户输入进行过滤,直接嵌入到 HTML 中
const html = `<h1>你搜索的关键词是: ${keyword}</h1>`;
res.send(html);
});
const port = 3000;
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
然后前端请求的地址是:
http://localhost:3000/search?keyword=<script>alert('你被 XSS 攻击了!')</script>
当个地址被加载之后,你的页面就会弹出一个窗口,也就是参数里面的script标签被执行了。
简单来说,就是你的页面被注入了一段可执行的js代码,称之为xss攻击。
另外xss攻击又被细分为3种:
- 持久型
- 非持久型
- dom型
不论类型是什么,xss攻击的核心都在于:一段非法js代码在你页面插入内容时,里面的script标签被执行了。
危害
从上面的例子可以看出,一旦被xss攻击注入代码,基本上比csrf攻击还要可怕,因为这个时候页面上的恶意代码触发一些接口的调用,甚至都可以拿到csrf令牌来伪造请求, 危害性可见非常大,而且现代前端项目中,像富文本处理非常普遍,这大大提高了攻击的可能性。
防范手段有哪些
CSRF攻击原理
我们可以看出来,攻击的主要目的就是让我们的页面执行一些不可信的js脚本,利用一些动态显示内容的地方来执行js代码。
防范手段
当我们知道攻击原理之后,防范主要就是一点:
- 前端对于动态插入内容的地方对内容进行检查、过滤(最常见的就时对富文本内容的展示之处)
对动态插入的内容也要做一些xss的攻击检查,检查的关键词包括:
<script>
<iframe>
<img>
eval()
…
在js中,用于过滤xss攻击除了自己处理这些数据以外,还可以借助三方包来处理,用的比较多的有:
- DOMPurify
Start 14.6k
- he
Start 3.5k
- sanitize-html
Start 3.9k
- js-xss
Start 5.3k
推荐前端项目中使用DOMPurify,因为他的更新维护比较及时。
安装DOMPurify
npm install dompurify
# 或者
yarn add dompurify
在react中使用DOMPurify处理插入内容
import React from 'react';
import DOMPurify from 'dompurify';
const App = () => {
const userInput = '<script>alert("XSS")</script>';
const filteredInput = DOMPurify.sanitize(userInput);
return (
<div>
{/* 使用dangerouslySetInnerHTML插入过滤后的内容 */}
<div dangerouslySetInnerHTML={{ __html: filteredInput }} />
</div>
);
};
export default App;
在vue中使用DOMPurify
<template>
<div>
<!-- 不推荐直接使用v-html插入用户输入 -->
<div v-html="filteredInput"></div>
</div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
data() {
return {
userInput: '<script>alert("XSS")</script>'
};
},
computed: {
filteredInput() {
// 使用DOMPurify过滤用户输入
return DOMPurify.sanitize(this.userInput);
}
}
};
</script>
效果
示例1
// 包含恶意脚本的输入
const maliciousInput = '<script>alert("XSS Attack!")</script><p>Hello, World!</p>';
// 使用 DOMPurify 进行净化
const cleanOutput = DOMPurify.sanitize(maliciousInput);
console.log('原始输入:', maliciousInput);
console.log('净化后的输出:', cleanOutput);
输出
- 原始输入:<script>alert(“XSS Attack!”)</script><p>Hello, World!</p>
- 净化后的输出:<p>Hello, World!</p>
示例2
// 包含危险属性的输入
const inputWithDangerousAttr = '<img src="javascript:alert(\'XSS\')" alt="Test">';
// 使用 DOMPurify 进行净化
const cleanOutputWithAttr = DOMPurify.sanitize(inputWithDangerousAttr);
console.log('原始输入:', inputWithDangerousAttr);
console.log('净化后的输出:', cleanOutputWithAttr);
输出
- 原始输入:<img src=“javascript:alert(‘XSS’)” alt=“Test”>
- 净化后的输出:<img alt=“Test”>
示例3
const input = '<p style="color: red;">Some text</p><span>Extra text</span><a href="javascript:alert(\'XSS\')">Link</a>';
const config = {
ALLOWED_TAGS: ['p', 'span'],
ALLOWED_ATTR: ['style']
};
const cleanOutputCustom = DOMPurify.sanitize(input, config);
console.log('原始输入:', input);
console.log('净化后的输出:', cleanOutputCustom);
输出
- 原始输入:<p style=“color: red;“>Some text</p><span>Extra text</span><a href=“javascript:alert(‘XSS’)“>Link</a>
- 净化后的输出:<p><span>Extra style=“color: red;“>Some text</p><span>Extra text</span>
服务端最好也不要大意
可以看到要防止xss攻击,只需要在前端代码动态插入内容的地方严防死守就可以了,但事实上,我更推荐大家在http请求提交到服务器的数据都要做好安全过滤,避免被注入一下非法代码被服务器执行, 同样也可以避免被注入的代码被其他用户请求到前端被执行(持久型xss攻击)。
在服务端对提交的数据也可以使用JSDOM, 同时需要借助jsdom包。
import { JSDOM } from 'jsdom';
import DOMPurify from 'dompurify';
const window = new JSDOM('').window;
const purify = DOMPurify(window);
const clean = purify.sanitize('<b>hello there</b>');
最后
尽量保证所有的依赖包都保持最新,因为依赖包都有可能爆出一些漏洞,如果你小众网站可能侥幸没人来搞你,但是最好时不抱有侥幸心理。 可以在这里查看一些公开的漏洞数据 https://github.com/advisories