乐闻世界logo
搜索文章和话题

How to implement CSRF protection in mobile applications and what are the special considerations?

2月19日 17:57

CSRF protection in mobile applications differs from web applications because mobile apps typically don't use the browser's automatic cookie sending mechanism, but still need to consider various security risks.

CSRF Specificity in Mobile Applications

1. Authentication Method Differences

Web Applications:

  • Use Cookie to store Session ID
  • Browser automatically sends cookies
  • Vulnerable to CSRF attacks

Mobile Applications:

  • Use Tokens (JWT, OAuth)
  • Manually manage authentication information
  • Relatively less vulnerable to traditional CSRF attacks
  • But has other security risks

2. Network Environment Differences

javascript
// Network challenges faced by mobile apps const mobileChallenges = { networkUnreliable: 'Network instability may lead to replay attacks', deviceCompromise: 'Device is rooted or jailbroken', appTampering: 'App is tampered with or repackaged', insecureStorage: 'Insecure local storage' };

Mobile Application CSRF Protection Strategies

1. Using Token Authentication (Recommended)

iOS Implementation

swift
// Swift - Token Authentication Management class TokenManager { static let shared = TokenManager() private let keychain = Keychain() func saveToken(_ token: String) { // Use Keychain to securely store Token keychain["authToken"] = token } func getToken() -> String? { return keychain["authToken"] } func clearToken() { keychain["authToken"] = nil } } // Network Request Manager class NetworkManager { static let shared = NetworkManager() private let session = URLSession.shared func request<T: Decodable>( _ endpoint: String, method: String = "GET", body: Data? = nil, completion: @escaping (Result<T, Error>) -> Void ) { guard let url = URL(string: endpoint) else { completion(.failure(NetworkError.invalidURL)) return } var request = URLRequest(url: url) request.httpMethod = method request.httpBody = body // Add authentication Token if let token = TokenManager.shared.getToken() { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } session.dataTask(with: request) { data, response, error in if let error = error { completion(.failure(error)) return } guard let data = data else { completion(.failure(NetworkError.noData)) return } do { let result = try JSONDecoder().decode(T.self, from: data) completion(.success(result)) } catch { completion(.failure(error)) } }.resume() } }

Android Implementation

java
// Java - Token Authentication Management public class TokenManager { private static TokenManager instance; private SharedPreferences preferences; private TokenManager(Context context) { preferences = context.getSharedPreferences("auth", Context.MODE_PRIVATE); } public static synchronized TokenManager getInstance(Context context) { if (instance == null) { instance = new TokenManager(context); } return instance; } public void saveToken(String token) { preferences.edit().putString("authToken", token).apply(); } public String getToken() { return preferences.getString("authToken", null); } public void clearToken() { preferences.edit().remove("authToken").apply(); } } // Network Request Interceptor public class AuthInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); // Add authentication Token String token = TokenManager.getInstance(context).getToken(); if (token != null) { Request authenticatedRequest = originalRequest.newBuilder() .header("Authorization", "Bearer " + token) .build(); return chain.proceed(authenticatedRequest); } return chain.proceed(originalRequest); } }

2. Device Fingerprinting

javascript
// Server-side - Device Fingerprint Verification class DeviceFingerprintService { async generateFingerprint(deviceInfo) { const fingerprintData = { deviceId: deviceInfo.deviceId, os: deviceInfo.os, osVersion: deviceInfo.osVersion, appVersion: deviceInfo.appVersion, screenResolution: deviceInfo.screenResolution, timestamp: Date.now() }; // Generate device fingerprint const fingerprint = crypto.createHash('sha256') .update(JSON.stringify(fingerprintData)) .digest('hex'); return fingerprint; } async validateFingerprint(userId, fingerprint) { const storedFingerprint = await this.getStoredFingerprint(userId); if (!storedFingerprint) { // First use, store fingerprint await this.storeFingerprint(userId, fingerprint); return true; } // Verify if fingerprint matches return storedFingerprint === fingerprint; } }

3. Request Signing

swift
// Swift - Request Signing class RequestSigner { static func signRequest(_ request: URLRequest, secretKey: String) -> URLRequest { var signedRequest = request // Generate timestamp and nonce let timestamp = String(Int(Date().timeIntervalSince1970)) let nonce = UUID().uuidString // Construct signature string let method = request.httpMethod ?? "GET" let url = request.url?.absoluteString ?? "" let body = request.httpBody?.base64EncodedString() ?? "" let signString = "\(method)\n\(url)\n\(timestamp)\n\(nonce)\n\(body)" // Generate HMAC-SHA256 signature let signature = signString.hmacSHA256(key: secretKey) // Add signature headers signedRequest.setValue(timestamp, forHTTPHeaderField: "X-Timestamp") signedRequest.setValue(nonce, forHTTPHeaderField: "X-Nonce") signedRequest.setValue(signature, forHTTPHeaderField: "X-Signature") return signedRequest } } // HMAC-SHA256 extension extension String { func hmacSHA256(key: String) -> String { let keyData = key.data(using: .utf8)! let messageData = self.data(using: .utf8)! var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH)) keyData.withUnsafeBytes { keyBytes in messageData.withUnsafeBytes { messageBytes in CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyBytes.baseAddress, keyData.count, messageBytes.baseAddress, messageData.count, &digestData) } } return digestData.base64EncodedString() } }
java
// Java - Request Signing public class RequestSigner { public static Request signRequest(Request request, String secretKey) { // Generate timestamp and nonce String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString(); // Construct signature string String method = request.method(); String url = request.url().toString(); String body = bodyToString(request.body()); String signString = method + "\n" + url + "\n" + timestamp + "\n" + nonce + "\n" + body; // Generate HMAC-SHA256 signature String signature = hmacSHA256(signString, secretKey); // Add signature headers return request.newBuilder() .addHeader("X-Timestamp", timestamp) .addHeader("X-Nonce", nonce) .addHeader("X-Signature", signature) .build(); } private static String hmacSHA256(String data, String key) { try { Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "HmacSHA256"); mac.init(secretKeySpec); byte[] hash = mac.doFinal(data.getBytes()); return Base64.encodeToString(hash, Base64.NO_WRAP); } catch (Exception e) { throw new RuntimeException("Failed to generate signature", e); } } }

4. Two-Factor Authentication

swift
// Swift - Two-Factor Authentication class TwoFactorAuthManager { static func verifyOTP(userId: String, otp: String, completion: @escaping (Bool) -> Void) { // Send OTP to server for verification let endpoint = "https://api.example.com/verify-otp" var request = URLRequest(url: URL(string: endpoint)!) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") let body = [ "userId": userId, "otp": otp ] request.httpBody = try? JSONSerialization.data(withJSONObject: body) URLSession.shared.dataTask(with: request) { data, response, error in if let httpResponse = response as? HTTPURLResponse { completion(httpResponse.statusCode == 200) } else { completion(false) } }.resume() } }

Server-side Verification

1. Token Verification Middleware

javascript
// Express.js - Token Verification Middleware const jwt = require('jsonwebtoken'); function authenticateMobile(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'No token provided' }); } const token = authHeader.substring(7); try { const decoded = jwt.verify(token, JWT_SECRET); req.user = decoded; next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } } // Request Signature Verification Middleware function verifySignature(req, res, next) { const timestamp = req.headers['x-timestamp']; const nonce = req.headers['x-nonce']; const signature = req.headers['x-signature']; if (!timestamp || !nonce || !signature) { return res.status(401).json({ error: 'Missing signature headers' }); } // Verify timestamp (prevent replay attacks) const now = Math.floor(Date.now() / 1000); if (Math.abs(now - parseInt(timestamp)) > 300) { // 5 minutes return res.status(401).json({ error: 'Request expired' }); } // Verify nonce (prevent replay attacks) if (isNonceUsed(nonce)) { return res.status(401).json({ error: 'Nonce already used' }); } // Verify signature const expectedSignature = generateSignature(req, timestamp, nonce); if (signature !== expectedSignature) { return res.status(401).json({ error: 'Invalid signature' }); } // Mark nonce as used markNonceAsUsed(nonce); next(); }

2. Device Fingerprint Verification

javascript
// Device Fingerprint Verification Middleware function verifyDeviceFingerprint(req, res, next) { const userId = req.user.id; const fingerprint = req.headers['x-device-fingerprint']; if (!fingerprint) { return res.status(401).json({ error: 'Device fingerprint required' }); } deviceFingerprintService.validateFingerprint(userId, fingerprint) .then(isValid => { if (!isValid) { return res.status(401).json({ error: 'Invalid device fingerprint' }); } next(); }) .catch(error => { res.status(500).json({ error: 'Fingerprint validation failed' }); }); }

Best Practices

1. Secure Storage

swift
// iOS - Using Keychain import Security class KeychainHelper { static func save(key: String, data: Data) -> Bool { let query = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecValueData as String: data ] as [String: Any] SecItemDelete(query as CFDictionary) return SecItemAdd(query as CFDictionary, nil) == errSecSuccess } static func load(key: String) -> Data? { let query = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecReturnData as String: true, kSecMatchLimit as String: kSecMatchLimitOne ] as [String: Any] var dataTypeRef: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) if status == errSecSuccess { return dataTypeRef as? Data } return nil } }
java
// Android - Using EncryptedSharedPreferences public class SecureStorage { private static EncryptedSharedPreferences preferences; public static void init(Context context) { try { MasterKey masterKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); preferences = (EncryptedSharedPreferences) EncryptedSharedPreferences.create( context, "secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); } catch (Exception e) { throw new RuntimeException("Failed to initialize secure storage", e); } } public static void saveString(String key, String value) { preferences.edit().putString(key, value).apply(); } public static String getString(String key) { return preferences.getString(key, null); } }

2. Certificate Pinning

swift
// iOS - SSL Certificate Pinning class CertificatePinning { static func validateCertificate(for serverTrust: SecTrust) -> Bool { // Get server certificate guard let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else { return false } // Get certificate data let serverCertificateData = SecCertificateCopyData(serverCertificate) as Data // Load local certificate guard let localCertificatePath = Bundle.main.path(forResource: "certificate", ofType: "cer"), let localCertificateData = try? Data(contentsOf: URL(fileURLWithPath: localCertificatePath)) else { return false } // Compare certificates return serverCertificateData == localCertificateData } }

CSRF protection in mobile applications needs to combine platform characteristics and security best practices to ensure effective security protection in various network environments and device states.

标签:CSRF