Vue 项目安全设置方案:XSS/CSRF 防护指南

在 Vue 项目中实施全面的 XSS 和 CSRF 防护需要前后端协同配合。以下是完整的实现方案:

Vue 前端安全配置方案

1. XSS 防御策略

安全数据渲染方案
vue 复制代码
<template>
  <div>
    <!-- 安全文本渲染(自动转义) -->
    <p>{{ userInput }}</p>
    
    <!-- HTML 内容渲染(使用 DOMPurify 净化) -->
    <div v-if="trustedContent" v-html="sanitizedHtml"></div>
  </div>
</template>

<script>
import DOMPurify from 'dompurify';

export default {
  data() {
    return {
      userInput: '<script>alert("XSS")<\/script>',
      trustedContent: '<b>安全内容</b>'
    };
  },
  computed: {
    sanitizedHtml() {
      // 使用 DOMPurify 净化 HTML
      return DOMPurify.sanitize(this.trustedContent, {
        ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'], // 仅允许安全标签
        ALLOWED_ATTR: ['href', 'title'] // 仅允许安全属性
      });
    }
  }
};
</script>
URL 参数安全处理
javascript 复制代码
// 在请求 URL 参数时进行编码
const unsafeValue = "<script>malicious</script>";
const safeParam = encodeURIComponent(unsafeValue);

this.$http.get(`/api/data?param=${safeParam}`);
内容安全策略 (CSP) 设置
javascript 复制代码
// 在 public/index.html 的 head 中添加 CSP meta 标签
<meta 
  http-equiv="Content-Security-Policy" 
  content="default-src 'self'; 
    script-src 'self' 'nonce-random123' https://trusted.cdn.com;
    style-src 'self' 'unsafe-inline';
    img-src 'self' data:; 
    connect-src 'self';
    font-src 'self';
    object-src 'none';
    frame-src 'none';
">

2. CSRF 防护策略

Axios 全局配置
javascript 复制代码
// src/utils/http.js
import axios from 'axios';
import store from '@/store';

const http = axios.create({
  baseURL: process.env.VUE_APP_API_BASE_URL,
  timeout: 10000,
});

// 请求拦截器:自动添加 CSRF Token
http.interceptors.request.use(config => {
  if (['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase())) {
    config.headers['X-CSRF-Token'] = store.state.csrfToken;
  }
  return config;
});

// 响应拦截器:处理 403 错误(CSRF 失效)
http.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 403) {
      store.dispatch('refreshCSRF'); // 刷新 CSRF Token
      // 可选:自动重新发送原始请求
      return http.request(error.config);
    }
    return Promise.reject(error);
  }
);

export default http;
CSRF Token 存储与刷新
javascript 复制代码
// Vuex store 模块
const securityModule = {
  namespaced: true,
  state: {
    csrfToken: null
  },
  mutations: {
    SET_CSRF_TOKEN(state, token) {
      // 基本验证
      if (typeof token === 'string' && token.length >= 32) {
        state.csrfToken = token;
      }
    }
  },
  actions: {
    async fetchCSRFToken({ commit }) {
      try {
        const response = await http.get('/api/csrf-token');
        commit('SET_CSRF_TOKEN', response.data.token);
        
        // 设置 token 到 meta 标签(可选,SSR 场景下)
        let meta = document.querySelector('meta[name="csrf-token"]');
        if (!meta) {
          meta = document.createElement('meta');
          meta.name = 'csrf-token';
          document.head.appendChild(meta);
        }
        meta.content = response.data.token;
      } catch (error) {
        console.error('获取 CSRF Token 失败', error);
      }
    }
  }
};

3. 路由安全防护

javascript 复制代码
// src/router/index.js
const router = new VueRouter({
  routes: [
    // ...
    {
      path: '/admin',
      component: AdminPanel,
      meta: { 
        requiresAuth: true,
        capabilities: ['admin']
      }
    }
  ]
});

router.beforeEach((to, from, next) => {
  // 检查认证需求
  if (to.matched.some(record => record.meta.requiresAuth)) {
    if (!store.getters.isAuthenticated) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      });
      return;
    }
    
    // 检查权限
    const requiredCapabilities = to.meta.capabilities || [];
    if (requiredCapabilities.length > 0 && 
        !store.getters.hasCapabilities(requiredCapabilities)) {
      next('/forbidden'); // 无权限页面
      return;
    }
  }
  
  // 每次路由切换滚动到顶部
  window.scrollTo(0, 0);
  
  next();
});

4. Vuex 安全实践

javascript 复制代码
// 增强安全性的 Vuex store
export default new Vuex.Store({
  state: {
    userData: null
  },
  mutations: {
    // 数据存储前的净化处理
    SET_USER_DATA(state, rawData) {
      // 执行深层次的净化
      state.userData = deepSanitize(rawData);
    }
  },
  actions: {
    // 安全获取用户数据
    async fetchUserData({ commit }, userId) {
      try {
        const response = await http.get(`/api/users/${userId}`);
        commit('SET_USER_DATA', response.data);
      } catch (error) {
        handleSecurityError(error);
      }
    }
  }
});

// 深层数据净化函数
function deepSanitize(data) {
  if (typeof data === 'string') {
    // 移除 HTML 标签
    return data.replace(/<[^>]*>/g, '');
  } 
  if (Array.isArray(data)) {
    return data.map(item => deepSanitize(item));
  }
  if (typeof data === 'object' && data !== null) {
    return Object.keys(data).reduce((acc, key) => {
      acc[key] = deepSanitize(data[key]);
      return acc;
    }, {});
  }
  return data;
}

// 安全错误处理
function handleSecurityError(error) {
  if (error.response) {
    switch (error.response.status) {
      case 401:
        // 处理未认证错误
        router.push('/login');
        break;
      case 403:
        // 处理未授权错误
        store.dispatch('handleForbidden');
        break;
      case 419:
        // CSRF Token 失效
        store.dispatch('refreshCSRF');
        break;
      default:
        console.error('API Error:', error);
    }
  }
}

服务端配合实现(Node.js 示例)

1. CSRF Token 生成与验证

javascript 复制代码
// 使用 Express + csurf 中间件
const express = require('express');
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const helmet = require('helmet');

const app = express();
app.use(cookieParser());
app.use(express.json());

// 设置 CSRF 保护
const csrfProtection = csrf({
  cookie: {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 86400 // 1 天
  }
});

// 提供 CSRF Token 端点
app.get('/api/csrf-token', csrfProtection, (req, res) => {
  res.json({ token: req.csrfToken() });
});

// 受保护的 API 端点
app.post('/api/update-profile', csrfProtection, (req, res) => {
  try {
    // 1. 验证输入(使用验证库)
    // 2. 处理业务逻辑
    // 3. 返回响应
    res.json({ success: true });
  } catch (error) {
    handleSecurityError(res, error);
  }
});

// 错误处理中间件
function handleSecurityError(res, error) {
  if (error.name === 'ValidationError') {
    res.status(400).json({ error: '无效请求' });
  } else {
    res.status(500).json({ error: '服务器错误' });
  }
}

2. HTTP 安全头设置

javascript 复制代码
// 使用 Helmet 设置安全头
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'nonce-random123'", "trusted-cdn.example.com"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      frameSrc: ["'none'"]
    }
  },
  xssFilter: true,
  noSniff: true,
  frameguard: { action: 'deny' },
  hsts: {
    maxAge: 31536000, // 1 年
    includeSubDomains: true
  },
  referrerPolicy: { policy: 'same-origin' }
}));

3. 输入验证与净化

javascript 复制代码
// 使用验证库,例如 Joi
const Joi = require('joi');

const userSchema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required(),
    
  email: Joi.string()
    .email({
      minDomainSegments: 2,
      tlds: { allow: ['com', 'net', 'org'] }
    })
    .required(),
    
  bio: Joi.string()
    .max(500)
    .optional()
    .escapeHTML() // 清除 HTML
});

app.post('/api/users', csrfProtection, (req, res) => {
  const { error, value } = userSchema.validate(req.body);
  
  if (error) {
    return res.status(400).json({ error: error.details[0].message });
  }
  
  // value 现在是安全、净化后的数据
  createUser(value);
});

项目部署安全配置

Nginx 示例配置

nginx 复制代码
server {
    listen 443 ssl;
    server_name yourdomain.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/privkey.pem;
    
    # Security headers
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-random123' trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none';";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Frame-Options "DENY";
    add_header X-XSS-Protection "1; mode=block";
    add_header Referrer-Policy "same-origin";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # Vue app
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
    
    # API proxy
    location /api/ {
        proxy_pass http://backend-service:3000/api/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
    }
}

最佳实践检查清单

XSS 防护

  • 所有动态 HTML 渲染使用 DOMPurify.sanitize()
  • 严格限制 v-html 指令的使用
  • 内容安全策略 (CSP) 正确配置并启用
  • URL 参数值使用 encodeURIComponent 处理
  • 禁用内联事件处理(如:onclick="...")
  • 设置 Cookie 的 HttpOnly 和 Secure 标志

CSRF 防护

  • 所有状态变更请求(POST/PUT/PATCH/DELETE)均包含 CSRF Token
  • Token 生成、传输和存储安全
  • Token 随每个请求变化(一次性使用最佳)
  • Cookie 设置 SameSite=Strict 或 Lax
  • API 端点验证 Origin/Referer

框架级防护

  • Vue 生产环境模式启用
  • 定期使用 npm audit 检查依赖漏洞
  • Vue Router 路由守卫实现权限控制
  • 禁用 Vue 配置中的 Devtools 在生产环境(Vue.config.devtools = false)

服务器协同

  • 所有响应设置安全头(X-Content-Type-Options, X-Frame-Options 等)
  • API 端点实施严格的输入验证
  • 敏感操作(如密码更改)增加二次认证
  • 使用 HTTPS 加密所有通信
  • API 启用 CORS 并严格配置白名单
  • 设置速率限制防止暴力破解

项目流程图

首页 子页面 未授权 已授权 GET 请求 状态变更请求 Token 无效 Token 有效 持续失败 用户访问 Vue 应用加载 获取 CSRF Token 检查路由权限 存储于 Vuex 跳转登录页 渲染页面 用户操作 直接发送 添加 CSRF Token 服务器验证 自动刷新 Token
并重试请求 执行操作 终止请求
提示用户

实际项目中应根据具体业务需求和安全等级要求进行调整。

相关推荐
安教安全员通关宝1 天前
2026年吉林安全员C3证考试题库+答案+解析
安全
躺柒1 天前
读数据安全架构设计与实践17安全架构案例(下)
数据库·安全·零信任·安全架构·防火墙·无边界
HackTwoHub1 天前
双通道红队 C2,MCP 接入 AI,全自动渗透实战利器,具备自动提权、进程伪装、无文件上线能力,载荷 AES 加密
安全·web安全·网络安全·系统安全·密码学·可信计算技术·安全架构
学页官方1 天前
【安全实操】教培系统上线前的安全检查清单(20条)
安全
每天题库1 天前
西藏安全员 A 证备考真题题库 强化重点知识刷题提升通过率
学习·安全·考试·题库·考证
2601_967212721 天前
安全型轨道式插座合规资质核查与工程选型技术要点
大数据·经验分享·安全
jimmyleeee1 天前
Agent安全之五:当语言变成执行------深入理解Unexpected Code Execution (ASI05)
人工智能·安全
Old Uncle Tom1 天前
LLM 替代 SAST?Claude Code 安全审查工具的设计取舍与工程启示
人工智能·安全·软件工程
console.log('npc')1 天前
周期任务功能完整实现:CRON 编辑器 + 模板变量 + 执行历史
javascript·vue.js·编辑器
安教安全员通关宝1 天前
2026年四川安全员A证考试题库+答案+解析
安全