安全防护
本文档介绍前端安全防护的最佳实践。
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;
});SameSite Cookie
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';">最佳实践
- 输入验证:所有用户输入都要验证
- 输出编码:避免 XSS 攻击
- HTTPS:使用 HTTPS 传输数据
- Token 验证:防止 CSRF 攻击
- 权限控制:前后端都要验证权限
- 敏感信息:不在前端存储敏感信息
- 依赖更新:定期更新依赖
- 安全审计:定期进行安全审计