Skip to content

安全防护

本文档介绍前端安全防护的最佳实践。

XSS 防护

避免使用 v-html

vue
<template>
  <!-- ✅ 推荐:使用 v-text -->
  <div v-text="userInput"></div>
  
  <!-- ❌ 不推荐:使用 v-html -->
  <div v-html="userInput"></div>
</template>

内容过滤

typescript
import DOMPurify from 'dompurify';

// 清理 HTML 内容
function sanitizeHtml(html: string) {
  return DOMPurify.sanitize(html);
}

CSRF 防护

Token 验证

typescript
// 请求拦截器添加 token
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
typescript
// 设置 Cookie
document.cookie = 'token=xxx; SameSite=Strict; Secure';

敏感信息保护

不在前端存储敏感信息

typescript
// ❌ 不推荐
localStorage.setItem('password', password);

// ✅ 推荐:只存储 token
localStorage.setItem('token', token);

加密存储

typescript
import CryptoJS from 'crypto-js';

const SECRET_KEY = 'your-secret-key';

// 加密
function encrypt(data: string) {
  return CryptoJS.AES.encrypt(data, SECRET_KEY).toString();
}

// 解密
function decrypt(encrypted: string) {
  const bytes = CryptoJS.AES.decrypt(encrypted, SECRET_KEY);
  return bytes.toString(CryptoJS.enc.Utf8);
}

权限控制

前端权限验证

typescript
// 路由守卫
router.beforeEach((to, from, next) => {
  const userStore = useUserStore();
  
  if (to.meta.requiresAuth && !userStore.isLogin) {
    next('/login');
    return;
  }
  
  if (to.meta.permissions) {
    const hasPermission = userStore.hasPermissions(to.meta.permissions);
    if (!hasPermission) {
      next('/403');
      return;
    }
  }
  
  next();
});

按钮权限

vue
<script setup lang="ts">
import { usePermission } from '@/hooks/usePermission';

const { hasPermission } = usePermission();
</script>

<template>
  <a-button v-if="hasPermission(['system:user:add'])">
    新增
  </a-button>
</template>

输入验证

表单验证

vue
<script setup lang="ts">
import { useVbenForm, z } from '#/adapter/form';

const [Form] = useVbenForm({
  schema: [
    {
      fieldName: 'email',
      label: '邮箱',
      component: 'Input',
      rules: z.string().email('请输入有效的邮箱地址')
    },
    {
      fieldName: 'phone',
      label: '手机号',
      component: 'Input',
      rules: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号')
    }
  ]
});
</script>

SQL 注入防护

typescript
// ✅ 推荐:使用参数化查询
const result = await request.get('/user/list', {
  params: { username }
});

// ❌ 不推荐:拼接 SQL
const sql = `SELECT * FROM users WHERE username = '${username}'`;

HTTPS

强制使用 HTTPS

typescript
// 检查协议
if (location.protocol !== 'https:') {
  location.href = 'https:' + window.location.href.substring(window.location.protocol.length);
}

安全请求头

typescript
// Nginx 配置
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000";

依赖安全

定期更新依赖

bash
# 检查过时的依赖
pnpm outdated

# 更新依赖
pnpm update

安全审计

bash
# 检查安全漏洞
pnpm audit

# 修复安全漏洞
pnpm audit fix

内容安全策略

CSP 配置

html
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; 
               script-src 'self' 'unsafe-inline'; 
               style-src 'self' 'unsafe-inline';">

最佳实践

  1. 输入验证:所有用户输入都要验证
  2. 输出编码:避免 XSS 攻击
  3. HTTPS:使用 HTTPS 传输数据
  4. Token 验证:防止 CSRF 攻击
  5. 权限控制:前后端都要验证权限
  6. 敏感信息:不在前端存储敏感信息
  7. 依赖更新:定期更新依赖
  8. 安全审计:定期进行安全审计

参考资源

MIT License