安全最佳实践 #
一、安全概述 #
1.1 安全威胁 #
| 威胁 | 说明 |
|---|---|
| 数据泄露 | 敏感数据被窃取 |
| 中间人攻击 | 网络通信被拦截 |
| 代码注入 | 恶意代码执行 |
| 反编译 | 应用被逆向工程 |
| 数据篡改 | 本地数据被修改 |
1.2 安全层级 #
text
应用层安全
├── 代码安全
├── 数据安全
└── 通信安全
平台层安全
├── iOS沙盒
└── Android沙盒
网络层安全
├── HTTPS
└── 证书校验
二、数据加密 #
2.1 加密工具类 #
typescript
// src/utils/encryption.ts
// 使用Web Crypto API
export class Encryption {
private static algorithm = 'AES-GCM';
private static keyLength = 256;
// 生成密钥
static async generateKey(): Promise<CryptoKey> {
return crypto.subtle.generateKey(
{
name: this.algorithm,
length: this.keyLength
},
true,
['encrypt', 'decrypt']
);
}
// 从密码派生密钥
static async deriveKey(password: string, salt: Uint8Array): Promise<CryptoKey> {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(password),
'PBKDF2',
false,
['deriveBits', 'deriveKey']
);
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256'
},
keyMaterial,
{ name: this.algorithm, length: this.keyLength },
false,
['encrypt', 'decrypt']
);
}
// 加密
static async encrypt(data: string, key: CryptoKey): Promise<{ iv: Uint8Array; data: Uint8Array }> {
const encoder = new TextEncoder();
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: this.algorithm, iv },
key,
encoder.encode(data)
);
return {
iv,
data: new Uint8Array(encrypted)
};
}
// 解密
static async decrypt(encrypted: { iv: Uint8Array; data: Uint8Array }, key: CryptoKey): Promise<string> {
const decrypted = await crypto.subtle.decrypt(
{ name: this.algorithm, iv: encrypted.iv },
key,
encrypted.data
);
const decoder = new TextDecoder();
return decoder.decode(decrypted);
}
// 导出密钥
static async exportKey(key: CryptoKey): Promise<string> {
const exported = await crypto.subtle.exportKey('raw', key);
return btoa(String.fromCharCode(...new Uint8Array(exported)));
}
// 导入密钥
static async importKey(keyData: string): Promise<CryptoKey> {
const binary = Uint8Array.from(atob(keyData), c => c.charCodeAt(0));
return crypto.subtle.importKey(
'raw',
binary,
{ name: this.algorithm, length: this.keyLength },
true,
['encrypt', 'decrypt']
);
}
}
2.2 安全存储服务 #
typescript
// src/services/secure-storage.service.ts
import { Preferences } from '@capacitor/preferences';
import { Encryption } from '../utils/encryption';
class SecureStorageService {
private key: CryptoKey | null = null;
private keyAlias = 'app_secure_key';
async init(password?: string): Promise<void> {
if (password) {
// 使用密码派生密钥
const salt = new TextEncoder().encode('app-salt-value');
this.key = await Encryption.deriveKey(password, salt);
} else {
// 使用或生成随机密钥
const storedKey = await this.getStoredKey();
if (storedKey) {
this.key = await Encryption.importKey(storedKey);
} else {
this.key = await Encryption.generateKey();
await this.storeKey(await Encryption.exportKey(this.key));
}
}
}
private async getStoredKey(): Promise<string | null> {
const { value } = await Preferences.get({ key: this.keyAlias });
return value;
}
private async storeKey(keyData: string): Promise<void> {
await Preferences.set({ key: this.keyAlias, value: keyData });
}
async set(key: string, value: any): Promise<void> {
if (!this.key) {
throw new Error('Secure storage not initialized');
}
const json = JSON.stringify(value);
const encrypted = await Encryption.encrypt(json, this.key);
// 存储加密数据
await Preferences.set({
key: `secure_${key}`,
value: JSON.stringify({
iv: Array.from(encrypted.iv),
data: Array.from(encrypted.data)
})
});
}
async get<T>(key: string): Promise<T | null> {
if (!this.key) {
throw new Error('Secure storage not initialized');
}
const { value } = await Preferences.get({ key: `secure_${key}` });
if (!value) return null;
try {
const parsed = JSON.parse(value);
const decrypted = await Encryption.decrypt(
{
iv: new Uint8Array(parsed.iv),
data: new Uint8Array(parsed.data)
},
this.key
);
return JSON.parse(decrypted) as T;
} catch {
return null;
}
}
async remove(key: string): Promise<void> {
await Preferences.remove({ key: `secure_${key}` });
}
}
export const secureStorage = new SecureStorageService();
2.3 使用示例 #
typescript
// 初始化(使用密码)
await secureStorage.init('user-password');
// 或初始化(使用随机密钥)
await secureStorage.init();
// 存储敏感数据
await secureStorage.set('user_token', {
access: 'xxx',
refresh: 'yyy',
expires: Date.now() + 3600000
});
// 读取敏感数据
const token = await secureStorage.get<{ access: string; refresh: string }>('user_token');
三、网络安全 #
3.1 HTTPS配置 #
typescript
// capacitor.config.json
{
"server": {
"iosScheme": "https",
"androidScheme": "https"
}
}
3.2 iOS网络安全 #
xml
<!-- ios/App/App/Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
<!-- 推荐设置:禁用任意HTTP加载 -->
<key>NSAllowsArbitraryLoads</key>
<false/>
<!-- 如果需要,为特定域名启用HTTP -->
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
3.3 Android网络安全 #
xml
<!-- android/app/src/main/res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- 生产环境:禁用明文HTTP -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- 开发环境:允许localhost -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
<!-- 证书固定(可选) -->
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set>
<pin digest="SHA-256">base64-encoded-pin</pin>
</pin-set>
</domain-config>
</network-security-config>
3.4 API请求封装 #
typescript
// src/services/api.service.ts
import { Preferences } from '@capacitor/preferences';
class ApiService {
private baseUrl = 'https://api.example.com';
private async getHeaders(): Promise<HeadersInit> {
const { value: token } = await Preferences.get({ key: 'auth_token' });
return {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : ''
};
}
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const headers = await this.getHeaders();
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
...headers,
...options.headers
}
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
get<T>(endpoint: string): Promise<T> {
return this.request<T>(endpoint);
}
post<T>(endpoint: string, data: any): Promise<T> {
return this.request<T>(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
export const apiService = new ApiService();
四、认证安全 #
4.1 Token管理 #
typescript
// src/services/auth.service.ts
import { Preferences } from '@capacitor/preferences';
import { secureStorage } from './secure-storage.service';
interface TokenData {
accessToken: string;
refreshToken: string;
expiresAt: number;
}
class AuthService {
private tokenKey = 'auth_tokens';
async login(credentials: { email: string; password: string }): Promise<void> {
// 调用登录API
const response = await fetch('https://api.example.com/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const data = await response.json();
// 安全存储token
await secureStorage.set(this.tokenKey, {
accessToken: data.accessToken,
refreshToken: data.refreshToken,
expiresAt: Date.now() + data.expiresIn * 1000
});
}
async getAccessToken(): Promise<string | null> {
const tokens = await secureStorage.get<TokenData>(this.tokenKey);
if (!tokens) return null;
// 检查是否过期
if (Date.now() >= tokens.expiresAt) {
// 尝试刷新token
const newTokens = await this.refreshTokens(tokens.refreshToken);
return newTokens?.accessToken || null;
}
return tokens.accessToken;
}
private async refreshTokens(refreshToken: string): Promise<TokenData | null> {
try {
const response = await fetch('https://api.example.com/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken })
});
const data = await response.json();
const tokens: TokenData = {
accessToken: data.accessToken,
refreshToken: data.refreshToken || refreshToken,
expiresAt: Date.now() + data.expiresIn * 1000
};
await secureStorage.set(this.tokenKey, tokens);
return tokens;
} catch {
// 刷新失败,清除token
await this.logout();
return null;
}
}
async logout(): Promise<void> {
await secureStorage.remove(this.tokenKey);
}
async isAuthenticated(): Promise<boolean> {
const token = await this.getAccessToken();
return token !== null;
}
}
export const authService = new AuthService();
4.2 生物识别 #
typescript
// 使用 @capacitor-community/biometric
import { BiometricAuth } from '@capacitor-community/biometric';
async function authenticateWithBiometric(): Promise<boolean> {
try {
const result = await BiometricAuth.checkBiometry();
if (!result.isAvailable) {
console.log('Biometric not available');
return false;
}
const authResult = await BiometricAuth.authenticate({
reason: '请验证身份以继续',
title: '身份验证',
subtitle: '使用生物识别',
cancelTitle: '取消'
});
return authResult.isAuthenticated;
} catch (error) {
console.error('Biometric auth error:', error);
return false;
}
}
五、代码安全 #
5.1 代码混淆 #
javascript
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
},
mangle: {
properties: {
regex: /^_/
}
}
}
}
});
5.2 环境变量安全 #
typescript
// .env(不提交到版本控制)
VITE_API_URL=https://api.example.com
VITE_API_KEY=your-api-key
// 使用
const apiUrl = import.meta.env.VITE_API_URL;
// 不要在前端存储敏感密钥
// 敏感操作应该在服务器端完成
5.3 输入验证 #
typescript
// src/utils/validation.ts
export function sanitizeInput(input: string): string {
return input
.replace(/[<>]/g, '') // 移除可能的HTML标签
.trim();
}
export function validateEmail(email: string): boolean {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
export function validatePhone(phone: string): boolean {
const regex = /^\+?[\d\s-]{10,}$/;
return regex.test(phone);
}
六、安全检查清单 #
6.1 开发阶段 #
- [ ] 所有API通信使用HTTPS
- [ ] 敏感数据加密存储
- [ ] 不在代码中硬编码密钥
- [ ] 输入验证和清理
- [ ] 错误信息不暴露敏感信息
6.2 构建阶段 #
- [ ] 启用代码混淆
- [ ] 移除console.log
- [ ] 检查依赖漏洞
- [ ] 环境变量正确配置
6.3 发布阶段 #
- [ ] 生产环境禁用调试
- [ ] 证书正确配置
- [ ] 安全策略配置
- [ ] 隐私政策完善
七、总结 #
7.1 安全要点 #
| 层级 | 措施 |
|---|---|
| 数据层 | 加密存储敏感数据 |
| 网络层 | 强制HTTPS |
| 认证层 | 安全Token管理 |
| 代码层 | 混淆和验证 |
7.2 下一步 #
了解安全最佳实践后,让我们学习 性能优化!
最后更新:2026-03-28