🌏 閱讀中文版本
Biometric Change Detection: Complete Implementation Guide for Android and iOS
Introduction
Biometric authentication (fingerprint, Face ID, Touch ID) has become a core security mechanism in modern mobile applications. However, how should apps detect when users add or remove biometric data to ensure security? This article provides an in-depth exploration of biometric change detection implementation methods on Android and iOS platforms, with complete code examples.
Why Detect Biometric Changes?
Security Considerations
When users add new fingerprints or reset Face ID, it may indicate:
- Device Ownership Transfer: Phone sold or lent to others
- Security Threats: Unauthorized personnel attempting to add their biometric data
- Data Access Risks: Newly added biometric data may be used to access sensitive information
Practical Application Scenarios
- Financial Applications: Banking apps need to require re-authentication when biometric settings change
- Enterprise Applications: MDM systems need to track device security status changes
- Health Applications: Medical apps need to ensure sensitive data is only accessible to authorized users
Android Biometric Detection Implementation
Core Concept: BiometricManager
Android provides the BiometricManager API to check biometric availability. The key method is canAuthenticate(), which returns the device’s current biometric status.
canAuthenticate() Return Values Explained
| Return Value | Description | Recommended Action |
|---|---|---|
BIOMETRIC_SUCCESS |
Biometric authentication available and configured | Proceed with biometric authentication |
BIOMETRIC_ERROR_NONE_ENROLLED |
Hardware supported but no biometric data enrolled | Prompt user to navigate to settings |
BIOMETRIC_ERROR_HW_UNAVAILABLE |
Hardware temporarily unavailable | Provide alternative authentication (PIN/password) |
BIOMETRIC_ERROR_NO_HARDWARE |
Device does not support biometric authentication | Use traditional authentication methods only |
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED |
Security update required | Prompt user to update system |
Implementation Example: Checking Biometric Availability
import android.content.Context;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricManager.Authenticators;
public class BiometricHelper {
public static BiometricStatus checkBiometricAvailability(Context context) {
BiometricManager biometricManager = BiometricManager.from(context);
int canAuthenticate = biometricManager.canAuthenticate(
Authenticators.BIOMETRIC_STRONG
);
switch (canAuthenticate) {
case BiometricManager.BIOMETRIC_SUCCESS:
return BiometricStatus.AVAILABLE;
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
return BiometricStatus.NOT_ENROLLED;
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
return BiometricStatus.TEMPORARILY_UNAVAILABLE;
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
return BiometricStatus.NOT_SUPPORTED;
case BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
return BiometricStatus.SECURITY_UPDATE_REQUIRED;
default:
return BiometricStatus.UNKNOWN;
}
}
public enum BiometricStatus {
AVAILABLE,
NOT_ENROLLED,
TEMPORARILY_UNAVAILABLE,
NOT_SUPPORTED,
SECURITY_UPDATE_REQUIRED,
UNKNOWN
}
}
Biometric Change Detection Strategies
Android does not provide a direct API to detect biometric data changes. In practice, the following strategies are needed:
Method 1: Periodic Status Checks (Recommended)
public class BiometricChangeDetector {
private static final String PREFS_NAME = "biometric_prefs";
private static final String KEY_LAST_STATUS = "last_biometric_status";
public static boolean hasBiometricChanged(Context context) {
SharedPreferences prefs = context.getSharedPreferences(
PREFS_NAME,
Context.MODE_PRIVATE
);
// Get current status
BiometricStatus currentStatus = BiometricHelper.checkBiometricAvailability(context);
// Get last recorded status
String lastStatus = prefs.getString(KEY_LAST_STATUS, null);
// First check
if (lastStatus == null) {
saveCurrentStatus(context, currentStatus);
return false;
}
// Compare status change
boolean changed = !currentStatus.name().equals(lastStatus);
if (changed) {
// Log change and update
Log.w("BiometricChange", "Biometric status changed from " +
lastStatus + " to " + currentStatus);
saveCurrentStatus(context, currentStatus);
}
return changed;
}
private static void saveCurrentStatus(Context context, BiometricStatus status) {
SharedPreferences prefs = context.getSharedPreferences(
PREFS_NAME,
Context.MODE_PRIVATE
);
prefs.edit()
.putString(KEY_LAST_STATUS, status.name())
.apply();
}
}
Method 2: Combined with Keystore Validation
// Create a key in Android Keystore that requires biometric authentication
// When biometric data changes, the key becomes invalidated
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import javax.crypto.KeyGenerator;
import java.security.KeyStore;
public class KeystoreBiometricDetector {
private static final String KEY_NAME = "biometric_key";
public static void createBiometricKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
);
keyGenerator.init(
new KeyGenParameterSpec.Builder(
KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setInvalidatedByBiometricEnrollment(true) // Key configuration
.build()
);
keyGenerator.generateKey();
}
public static boolean isBiometricKeyValid() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
return keyStore.containsAlias(KEY_NAME);
} catch (Exception e) {
return false;
}
}
}
iOS Biometric Detection Implementation
Core Concept: evaluatedPolicyDomainState
iOS’s LocalAuthentication framework provides the evaluatedPolicyDomainState property to detect Touch ID or Face ID configuration changes. This property returns a Data object that changes when biometric data is modified.
Implementation Example: Detecting Biometric Changes
import LocalAuthentication
class BiometricChangeDetector {
private static let kBiometricStateKey = "biometric_domain_state"
// Check if biometric authentication is available
static func canUseBiometric() -> Bool {
let context = LAContext()
var error: NSError?
return context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error
)
}
// Get current biometric state
static func getCurrentBiometricState() -> Data? {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error
) else {
return nil
}
return context.evaluatedPolicyDomainState
}
// Check if biometric configuration has changed
static func hasBiometricChanged() -> Bool {
guard let currentState = getCurrentBiometricState() else {
// Biometric unavailable or not enrolled
return false
}
// Get previously saved state
guard let savedState = UserDefaults.standard.data(
forKey: kBiometricStateKey
) else {
// First check, save current state
saveBiometricState(currentState)
return false
}
// Compare state changes
if currentState != savedState {
print("⚠️ Biometric configuration has changed!")
saveBiometricState(currentState)
return true
}
return false
}
// Save biometric state
private static func saveBiometricState(_ state: Data) {
UserDefaults.standard.set(state, forKey: kBiometricStateKey)
}
// Clear saved state (when user logs out)
static func clearBiometricState() {
UserDefaults.standard.removeObject(forKey: kBiometricStateKey)
}
}
Practical Application Example
// Check biometric changes on app launch
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidBecomeActive(_ application: UIApplication) {
checkBiometricSecurity()
}
private func checkBiometricSecurity() {
if BiometricChangeDetector.hasBiometricChanged() {
// Biometric configuration changed, execute security measures
handleBiometricChange()
}
}
private func handleBiometricChange() {
// 1. Log security event
SecurityLogger.log("Biometric configuration changed")
// 2. Clear sensitive data cache
clearSensitiveDataCache()
// 3. Require user re-authentication
presentReauthenticationScreen()
// 4. Optional: Notify backend server
notifyServerBiometricChanged()
}
private func clearSensitiveDataCache() {
// Clear locally cached sensitive data
KeychainHelper.clearAll()
UserDefaults.standard.removeObject(forKey: "cached_token")
}
private func presentReauthenticationScreen() {
// Display re-authentication screen
let alert = UIAlertController(
title: "Security Change Detected",
message: "Biometric configuration has changed. Please re-authenticate.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Re-authenticate", style: .default) { _ in
// Navigate to authentication screen
self.showAuthenticationScreen()
})
window?.rootViewController?.present(alert, animated: true)
}
}
Platform Differences Comparison
| Feature | Android | iOS |
|---|---|---|
| Detection Method | Status polling + Keystore invalidation | evaluatedPolicyDomainState comparison |
| Real-time | Requires active checking | Requires active checking |
| Privacy Protection | Does not reveal specific changes | Does not reveal specific changes |
| API Support | BiometricManager (API 23+) | LocalAuthentication (iOS 8+) |
| Change Granularity | Status change (available/unavailable) | Data change (add/remove fingerprint) |
| Implementation Complexity | Medium (requires Keystore integration) | Simple (direct state comparison) |
Security Best Practices
1. Change Detection Timing
- App Launch:
onCreate()orapplicationDidBecomeActive() - Before Sensitive Operations: Before accessing financial data or modifying settings
- Periodic Background Checks: Check at regular intervals (e.g., every 24 hours)
2. Actions After Change Detection
1. Log security event (timestamp, device information)
2. Clear local sensitive data cache
3. Require user re-authentication
4. Notify backend server (optional)
5. Update stored biometric status
3. Privacy Compliance
- Inform Users: Explain biometric data handling in privacy policy
- Obtain Consent: Get explicit consent when first using biometric authentication
- Minimize Storage: Only store necessary status information (e.g., hash values), not raw biometric data
- Secure Storage: Use Keychain (iOS) or EncryptedSharedPreferences (Android)
Frequently Asked Questions
Q1: Why is it necessary to detect biometric changes?
A: When users add or remove biometric data, it may indicate device ownership transfer or security threats. Sensitive applications such as financial, enterprise, and medical apps need to detect these changes to ensure data security.
Q2: Can Android directly obtain specific change details (e.g., which fingerprint was deleted)?
A: No. For privacy protection, Android only provides overall status (available/unavailable) and does not reveal which specific biometric data was changed.
Q3: When does iOS’s evaluatedPolicyDomainState change?
A: It changes when:
- Touch ID fingerprints are added or removed
- Face ID is reset
- Biometric authentication is disabled
Q4: What actions should be taken after detecting a change?
A: Recommended measures:
- Log security event
- Clear sensitive data cache
- Require user re-authentication
- Notify backend server (recommended for financial apps)
Q5: How often should biometric changes be checked?
A: Recommended strategy:
- Check on each app launch
- Check before entering sensitive features
- Periodic background checks (every 24 hours)
Q6: How to implement more precise change detection on Android?
A: Combine the following methods:
- Use Keystore keys with
setInvalidatedByBiometricEnrollment(true) - Regularly check BiometricManager status
- Record status change timestamps to analyze abnormal patterns
Q7: Should biometric status be synced to backend server?
A: Depends on application type:
- Financial Applications: Recommended to sync, aids risk management
- General Applications: Optional, mainly handle locally
- Enterprise Applications: Recommended to sync for compliance requirements
Summary
Biometric change detection is a crucial component of mobile application security architecture. While Android and iOS provide different implementation methods, the core concept remains the same: detecting biometric configuration changes while protecting user privacy.
Key Takeaways:
- Android: Combine
BiometricManagerstatus checks with Keystore key invalidation mechanism - iOS: Use
evaluatedPolicyDomainStatecomparison to detect changes - Privacy Protection: Both platforms do not reveal specific change details
- Security Practices: Clear cache and require re-authentication when changes detected
- Compliance Requirements: Follow privacy policies and obtain user consent
As biometric technology evolves and privacy regulations strengthen, regularly consult Android and iOS official documentation to ensure implementations meet the latest standards.
Related Articles
- Secure Guest Mode Data Recording in iOS and Android Applications
- iOS vs Android Deep Link Complete Comparison Guide: In-Depth Analysis of Universal Links & App Links
- iOS vs Android Deep Link 完整比較指南:Universal Links 與 App Links 深度解析
- iOS SSL Pinning and Certificate Validation Guide
- Flutter vs React Native: In-Depth Analysis and Selection Guide