敏感数据安全处理
场景
前端页面需要展示和处理用户手机号、身份证号、银行卡号等敏感信息,如何确保安全?
方案设计
1. 数据脱敏展示
data-mask.ts
const maskRules = {
// 手机号:138****5678
phone: (val: string) => val.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
// 身份证:110***********1234
idCard: (val: string) => val.replace(/(\d{3})\d{11}(\d{4})/, '$1***********$2'),
// 银行卡:**** **** **** 5678
bankCard: (val: string) => `**** **** **** ${val.slice(-4)}`,
// 姓名:张*三
name: (val: string) => {
if (val.length <= 2) return val[0] + '*';
return val[0] + '*'.repeat(val.length - 2) + val[val.length - 1];
},
// 邮箱:z***n@example.com
email: (val: string) => {
const [user, domain] = val.split('@');
return `${user[0]}***${user[user.length - 1]}@${domain}`;
},
};
function mask(type: keyof typeof maskRules, value: string): string {
if (!value) return '';
return maskRules[type](value);
}
2. 前端存储安全
secure-storage.ts
// ❌ 永远不要做
localStorage.setItem('token', accessToken); // Token 直接存 localStorage
localStorage.setItem('password', password); // 密码存前端
// ✅ Token 存储最佳实践
// 方案 1:HttpOnly Cookie(推荐,JS 无法读取)
// Set-Cookie: access_token=xxx; HttpOnly; Secure; SameSite=Strict
// 方案 2:如果必须存前端,用内存存储
class TokenStore {
private token: string | null = null;
setToken(token: string) { this.token = token; }
getToken() { return this.token; }
clearToken() { this.token = null; }
}
// 页面刷新后需要用 refresh_token 重新获取
3. 传输安全
encryption.ts
// 敏感数据传输加密(如密码)
// 前端用 RSA 公钥加密,后端用私钥解密
async function encryptPassword(password: string, publicKey: string): Promise<string> {
const key = await crypto.subtle.importKey(
'spki',
base64ToArrayBuffer(publicKey),
{ name: 'RSA-OAEP', hash: 'SHA-256' },
false,
['encrypt']
);
const encrypted = await crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
key,
new TextEncoder().encode(password)
);
return arrayBufferToBase64(encrypted);
}
function base64ToArrayBuffer(base64: string): ArrayBuffer {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes.buffer;
}
function arrayBufferToBase64(buffer: ArrayBuffer): string {
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}
4. 安全检查清单
| 场景 | 风险 | 措施 |
|---|---|---|
| 展示敏感信息 | 数据泄露 | 脱敏显示、点击查看完整 |
| 存储 Token | XSS 窃取 | HttpOnly Cookie / 内存存储 |
| 密码传输 | 中间人截获 | HTTPS + RSA 加密 |
| 接口返回 | 过度暴露 | 后端按需返回、最小权限 |
| Console 输出 | 信息泄露 | 生产环境移除 console.log |
| 错误日志 | 含敏感信息 | 上报前过滤脱敏 |
常见面试问题
Q1: 前端能做加密吗?有意义吗?
答案:
前端加密有一定意义但不绝对可靠:
- 有意义:防止中间人明文截获(HTTPS 已覆盖)、防止后端日志记录明文密码、防止浏览器扩展嗅探
- 局限性:前端代码可被查看、密钥管理困难、无法防止中间人替换公钥
核心原则:安全不能只靠前端,HTTPS 是底线,前端加密是额外防护层。
Q2: Token 存在哪里最安全?
答案:
| 方案 | 安全性 | XSS 风险 | CSRF 风险 |
|---|---|---|---|
| HttpOnly Cookie | ★★★ | 免疫 | 需防护 |
| 内存变量 | ★★★ | 较安全 | 免疫 |
| localStorage | ★ | 易被窃取 | 免疫 |
| sessionStorage | ★★ | 易被窃取 | 免疫 |
推荐:Access Token 放 HttpOnly Cookie,Refresh Token 也放 HttpOnly Cookie(设 Path=/api/refresh)。