前端安全问题
按照 MECE(相互独立,完全穷尽) 原则,全面列出前端安全问题,可以从以下维度进行分类:输入验证、数据传输、资源加载、身份认证与授权、浏览器特性 和 依赖及配置。
常用总结
- 输入验证与处理: 前端对与后端进行交互的数据进行验证和处理(包括字符长度、类型、文件大小等),防止恶意输入导致的安全问题。
核心点:不符合要求的数据,过滤掉,不传给后端。
- 数据传输: 前端与后端进行数据传输时(不要使用硬编码将项目敏感信息泄露),需要确保数据的安全性,防止数据被窃取或篡改。
核心点:敏感信息,不要直接传输,需要加密。
- 身份认证与授权:前端需要与后端进行身份认证和授权,确保只有合法用户才能访问敏感信息或执行敏感操作。
核心点:资源谁能访问,怎么访问,访问权限要严格控制。
- 请求限制:前端需要限制请求次数,防止恶意请求导致的服务器压力。
核心点:限制请求次数,防止恶意请求。
一、输入验证与处理
- 跨站脚本攻击(XSS)
解决方案:使用框架自带的防 XSS 功能(如 Vue.js 和 React 的模板自动转义)。
- 直接插入恶意脚本导致数据窃取或页面劫持。
- 类型:存储型、反射型、DOM 型。
- 防范:对输入进行转义、使用内容安全策略(CSP)。
- SQL 注入
- 未验证的输入被拼接成 SQL 语句,影响数据库安全。
- 防范:使用参数化查询或预编译语句,或 ORM 框架。
- 命令注入
- 用户输入被用作命令执行参数,导致服务器被攻击。
- 防范:对输入进行过滤,避免使用用户输入构建命令。
- 路径遍历攻击
- 未验证的文件路径输入可能导致访问非授权文件。
- 防范:对文件路径进行验证和规范化。
- 输入过长攻击
- 输入长度未限制,导致内存溢出或拒绝服务。
- 防范:限制输入长度,使用流式处理。
- 文件上传漏洞
- 上传恶意文件如脚本文件,可能被执行。
- 防范:验证文件类型和内容,限制文件大小。
二、数据传输与存储
- 明文传输
- 数据通过 HTTP 明文传输,易被窃听和篡改。
- 防范:使用 HTTPS 加密传输。
- 中间人攻击(MITM)
- 数据传输过程中被拦截并修改。
- 防范:使用 HTTPS,验证证书。
- Cookie 泄露
- 不安全的 Cookie 被窃取,导致会话劫持。
- 防范:设置 HttpOnly 和 Secure 标志。
- 本地存储泄露
- 本地存储中保存敏感信息,易被恶意脚本或浏览器扩展窃取。
- 防范:使用 HTTPS,限制存储内容。
- JSON 劫持
- JSON 数据被外部脚本获取并利用。
- 防范:使用 Content-Type 头部设置为 application/json,避免 JSONP。
- Token 曝露
- API Token 等敏感信息硬编码到前端或泄露。
- 防范:避免硬编码,使用环境变量或配置文件。
三、资源加载与依赖
- 第三方资源攻击
- 引入的第三方脚本、库或框架中包含恶意代码。
- 防范:验证第三方资源来源,使用内容安全策略(CSP)。
- 依赖漏洞
- 使用的第三方库存在已知漏洞。
- 防范:定期更新依赖,使用依赖管理工具。
- 资源完整性破坏
- 外部资源被篡改为恶意版本。
- 防范:使用内容哈希或数字签名验证资源完整性。
- 恶意 CDN 攻击
- 不可信的 CDN 服务提供篡改后的资源。
- 防范:选择可靠的 CDN 服务,验证资源来源。
- 动态资源加载
- 动态插入的外部资源可能被恶意利用。
- 防范:验证动态加载的资源,限制资源类型。
四、身份认证与授权
- 跨站请求伪造(CSRF)
- 利用用户登录态发送恶意请求。
- 防范:验证 Referer 头部,使用 CSRF Token。
- 暴力破解
- 使用自动化脚本尝试破解用户密码。
- 防范:限制登录尝试次数,使用验证码。
- Session 劫持
- 窃取用户的会话 ID 并冒充用户。
- 防范:使用安全的会话 ID,定期更换会话 ID。
- 权限提升
- 利用漏洞获得高于实际授权的权限。
- 防范:严格权限控制,限制用户操作。
- OAuth 2.0 相关漏洞
- 未验证的重定向 URL 或 Token 曝露。
- 防范:验证重定向 URL,限制 Token 权限。
- 多角色用户数据泄露
- 前端未正确区分用户角色的数据展示。
- 防范:根据用户角色展示数据,避免敏感信息泄露。
五、浏览器特性滥用
- 点击劫持(Clickjacking)
- 页面嵌套透明 iframe 诱导用户操作。
- 防范:使用 X-Frame-Options 头部,限制页面嵌入。
- DOM 劫持
- 恶意扩展或脚本篡改页面 DOM 结构。
- 防范:验证 DOM 操作,限制脚本权限。
- 浏览器缓存敏感数据
- 敏感数据被缓存,可能被不可信用户获取。
- 防范:使用 HTTP 缓存控制头部,避免缓存敏感数据。
- 窗口劫持(Tabnabbing)
- 通过替换窗口内容钓鱼用户。
- 防范:使用 Content-Security-Policy 头部,限制窗口内容来源。
- 服务工作者滥用
- 攻击者通过劫持 Service Worker 控制离线资源。
- 防范:验证 Service Worker 的来源,限制其权限。
六、配置与部署问题
- CSP 配置不当
Content-Security-Policy
配置错误,未限制可信来源。- 防范:正确配置 CSP,限制可信资源。
- 不安全的 HTTP Header
- 未设置安全响应头(如
X-Frame-Options
,X-Content-Type-Options
)。 - 防范:设置安全响应头,增强页面安全性。
- 未设置安全响应头(如
- 缓存配置不当
- 敏感页面或数据被客户端缓存。
- 防范:设置合适的缓存策略,避免敏感数据缓存。
- 错误日志泄露
- 部署环境暴露调试信息或错误日志。
- 防范:避免在生产环境中暴露调试信息,限制错误日志输出。
- API 接口暴露
- 前端代码泄露了后端接口,未加以隐藏。
- 防范:使用混淆或加密技术保护 API 接口。
- 默认配置使用
- 使用框架或工具的默认配置导致安全漏洞。
- 防范:检查并更新框架或工具的配置,避免使用默认配置。
七、其他潜在风险
- 社交工程攻击
- 利用社会信任传播恶意链接或脚本。
- 防范:提高用户安全意识,避免点击不明链接。
- 恶意脚本扩展
- 用户安装的恶意浏览器扩展干扰页面行为。
- 防范:定期更新和审查浏览器扩展,避免安装未知扩展。
- 时间与流量分析攻击
- 分析页面响应时间或流量模式获取敏感信息。
- 防范:使用加密通信,防止流量分析。
- 拒绝服务攻击(DoS)
- 大量请求导致页面或服务器无法响应。
- 防范:使用负载均衡和限流策略,防止 DoS 攻击。
- WebSocket 滥用
- 不安全的 WebSocket 通信被劫持。
- 防范:使用 WSS 协议,并验证 WebSocket 连接。
八、常见的具体实践
基于 输入验证与处理、数据传输、身份认证与授权、请求限制 的四个核心安全点,在 Vue 3 项目中可以通过以下代码示例来实现安全设计:
1. 输入验证与处理
实现目标: 确保用户输入数据符合预期格式,防止 XSS、SQL 注入、命令注入等问题。
vue
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="username"
type="text"
maxlength="20"
placeholder="Enter your username"
/>
<input v-model="email" type="email" placeholder="Enter your email" />
<button type="submit">Submit</button>
</form>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const username = ref("");
const email = ref("");
const handleSubmit = () => {
// 1. 前端验证输入
if (!/^[a-zA-Z0-9]{3,20}$/.test(username.value)) {
alert("Invalid username. Use 3-20 alphanumeric characters.");
return;
}
if (!/^\S+@\S+\.\S+$/.test(email.value)) {
alert("Invalid email format.");
return;
}
// 2. 数据发送到后端
const payload = {
username: username.value.trim(),
email: email.value.trim(),
};
fetch("/api/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
};
return { username, email, handleSubmit };
},
};
</script>
关键点:
- 使用正则表达式和
maxlength
限制输入内容。 - 对输入数据进行
trim
去除多余空格。 - 在提交给后端之前再次验证。
2. 数据传输
实现目标: 加密敏感信息,防止数据被窃取或篡改。
javascript
// utils/encryption.js
import CryptoJS from "crypto-js";
// 对敏感数据进行加密
export const encryptData = (data, secretKey) => {
return CryptoJS.AES.encrypt(JSON.stringify(data), secretKey).toString();
};
// 解密数据
export const decryptData = (encryptedData, secretKey) => {
const bytes = CryptoJS.AES.decrypt(encryptedData, secretKey);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
};
vue
<script>
import { encryptData } from "@/utils/encryption";
export default {
setup() {
const secretKey = "your-secret-key"; // 建议从后端获取
const sendData = async () => {
const sensitiveData = { creditCard: "1234-5678-9876-5432", cvv: "123" };
const encryptedPayload = encryptData(sensitiveData, secretKey);
await fetch("/api/secure-endpoint", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ data: encryptedPayload }),
});
};
return { sendData };
},
};
</script>
关键点:
- 敏感数据通过 AES 加密后传输。
- 后端解密后再处理敏感信息。
3. 身份认证与授权
实现目标: 使用 Token 验证用户身份,限制访问权限。
javascript
// api/auth.js
import axios from "axios";
const apiClient = axios.create({
baseURL: "/api",
headers: {
"Content-Type": "application/json",
},
});
// 添加请求拦截器(附加 Token)
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem("authToken");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 用户登录
export const login = async (credentials) => {
const response = await apiClient.post("/login", credentials);
const token = response.data.token;
localStorage.setItem("authToken", token); // 存储 Token
return token;
};
// 获取用户信息
export const getUserInfo = async () => {
return await apiClient.get("/user");
};
vue
<script>
import { login, getUserInfo } from "@/api/auth";
export default {
setup() {
const handleLogin = async () => {
try {
const token = await login({ username: "user", password: "pass" });
console.log("Logged in successfully, token:", token);
// 获取用户信息
const userInfo = await getUserInfo();
console.log("User info:", userInfo.data);
} catch (error) {
console.error("Login failed:", error);
}
};
return { handleLogin };
},
};
</script>
关键点:
- 使用
localStorage
或sessionStorage
存储 Token。 - 在 API 请求中通过拦截器自动附加 Token。
4. 请求限制
实现目标: 防止恶意请求(如暴力破解、DoS 攻击)。
vue
<script>
import axios from "axios";
import _ from "lodash";
export default {
setup() {
// 限制请求频率
const rateLimitedFetch = _.throttle(async () => {
try {
const response = await axios.get("/api/rate-limited-endpoint");
console.log(response.data);
} catch (error) {
console.error("Request failed:", error);
}
}, 3000); // 每 3 秒允许发送一次请求
return { rateLimitedFetch };
},
};
</script>
关键点:
- 使用
lodash
的throttle
或debounce
限制请求频率。 - 服务端同样需要配合限流策略(如 Redis 记录用户请求频率)。