5月27日 16:46
What are the best practices for Expo app security and data protection?
Security and data protection in Expo apps are important aspects that cannot be ignored during development. With the increase in mobile app security threats, developers need to take multi-layered security measures to protect user data and privacy.
Data Security Strategies:
- Sensitive Data Storage
Use expo-secure-store to store sensitive information:
typescriptimport * as SecureStore from 'expo-secure-store'; // Save sensitive data async function saveToken(token: string) { try { await SecureStore.setItemAsync('userToken', token, { keychainAccessible: SecureStore.WHEN_UNLOCKED, }); } catch (error) { console.error('Failed to save token:', error); } } // Read sensitive data async function getToken(): Promise<string | null> { try { return await SecureStore.getItemAsync('userToken'); } catch (error) { console.error('Failed to get token:', error); return null; } } // Delete sensitive data async function deleteToken() { try { await SecureStore.deleteItemAsync('userToken'); } catch (error) { console.error('Failed to delete token:', error); } }
- Encrypted Communication
Use HTTPS and certificate pinning:
typescriptimport * as SecureStore from 'expo-secure-store'; // Configure certificate pinning const fetchWithCertificatePinning = async (url: string) => { try { const response = await fetch(url, { headers: { 'Content-Type': 'application/json', }, }); return response.json(); } catch (error) { console.error('Network error:', error); throw error; } };
- API Key Management
Avoid hardcoding API keys in client:
typescript// Use environment variables const API_KEY = process.env.EXPO_PUBLIC_API_KEY; // Or use backend proxy const fetchSecureData = async () => { const response = await fetch('https://api.example.com/data', { headers: { 'Authorization': `Bearer ${await getToken()}`, }, }); return response.json(); };
Authentication and Authorization:
- JWT Token Management
typescriptimport * as SecureStore from 'expo-secure-store'; // Save JWT token async function saveAuthToken(token: string) { await SecureStore.setItemAsync('authToken', token); } // Get JWT token async function getAuthToken(): Promise<string | null> { return await SecureStore.getItemAsync('authToken'); } // Refresh token async function refreshToken(): Promise<string> { const refreshToken = await SecureStore.getItemAsync('refreshToken'); const response = await fetch('https://api.example.com/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }), }); const { token } = await response.json(); await saveAuthToken(token); return token; }
- OAuth Integration
typescriptimport * as WebBrowser from 'expo-web-browser'; import * as AuthSession from 'expo-auth-session'; // OAuth authentication flow const discovery = { authorizationEndpoint: 'https://auth.example.com/authorize', tokenEndpoint: 'https://auth.example.com/token', }; async function authenticate() { const request = new AuthSession.AuthRequest({ clientId: 'your-client-id', scopes: ['openid', 'profile'], redirectUri: AuthSession.makeRedirectUri({ scheme: 'myapp', }), }); const result = await request.promptAsync(discovery); if (result.type === 'success') { const { accessToken } = result.params; await saveAuthToken(accessToken); return accessToken; } }
Network Security:
- HTTPS Enforcement
typescript// Ensure all network requests use HTTPS const secureFetch = async (url: string, options?: RequestInit) => { if (!url.startsWith('https://')) { throw new Error('Only HTTPS requests are allowed'); } return fetch(url, options); };
- Request Validation
typescript// Validate response data interface ApiResponse<T> { data: T; success: boolean; message?: string; } async function fetchValidatedData<T>(url: string): Promise<T> { const response = await fetch(url); const data: ApiResponse<T> = await response.json(); if (!data.success) { throw new Error(data.message || 'Request failed'); } return data.data; }
- CSRF Protection
typescript// Use CSRF token async function fetchWithCSRF(url: string, options?: RequestInit) { const csrfToken = await SecureStore.getItemAsync('csrfToken'); return fetch(url, { ...options, headers: { ...options?.headers, 'X-CSRF-Token': csrfToken || '', }, }); }
Input Validation:
- Form Validation
typescript// Validate email format const validateEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; // Validate password strength const validatePassword = (password: string): boolean => { // At least 8 characters, contains uppercase, lowercase and numbers const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/; return passwordRegex.test(password); }; // Validate phone number const validatePhone = (phone: string): boolean => { const phoneRegex = /^1[3-9]\d{9}$/; return phoneRegex.test(phone); };
- XSS Protection
typescript// Escape HTML special characters const escapeHtml = (unsafe: string): string => { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }; // Safely render user input function SafeText({ text }: { text: string }) { const safeText = escapeHtml(text); return <Text>{safeText}</Text>; }
App Security Configuration:
- app.json Security Configuration
json{ "expo": { "ios": { "bundleIdentifier": "com.yourcompany.yourapp", "infoPlist": { "NSAppTransportSecurity": { "NSAllowsArbitraryLoads": false } } }, "android": { "package": "com.yourcompany.yourapp", "permissions": [] }, "extra": { "eas": { "projectId": "your-project-id" } } } }
- Environment Variable Management
bash# .env file EXPO_PUBLIC_API_URL=https://api.example.com EXPO_PUBLIC_API_KEY=your-api-key
typescript// Use environment variables const API_URL = process.env.EXPO_PUBLIC_API_URL; const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
Logging and Monitoring:
- Error Tracking
typescriptimport * as Sentry from '@sentry/react-native'; // Configure Sentry Sentry.init({ dsn: 'your-sentry-dsn', environment: __DEV__ ? 'development' : 'production', }); // Capture errors try { // Code that might fail } catch (error) { Sentry.captureException(error); }
- Security Logging
typescript// Log security events const logSecurityEvent = async (event: string, details: any) => { if (!__DEV__) { await fetch('https://logs.example.com/security', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event, details, timestamp: Date.now() }), }); } };
Best Practices:
- Principle of Least Privilege: Only request necessary permissions
- Data Minimization: Only collect and store necessary data
- Encrypted Transmission: Use HTTPS for all network communications
- Secure Storage: Use encrypted storage for sensitive data
- Regular Audits: Conduct regular security audits and penetration testing
- User Education: Educate users about security risks
Common Security Threats:
- Man-in-the-Middle Attacks: Use HTTPS and certificate pinning
- Data Leaks: Encrypt sensitive data
- Reverse Engineering: Use code obfuscation
- Replay Attacks: Use timestamps and nonces
- SQL Injection: Use parameterized queries
By implementing these security measures, you can significantly improve the security of Expo apps and protect user data and privacy.