Menyimpan token autentikasi (JWT, session token, atau refresh token) di dalam AsyncStorage adalah celah keamanan kritikal yang sering ditemui pada aplikasi React Native. AsyncStorage menyimpan data dalam bentuk file teks biasa (plaintext) atau database SQLite tanpa enkripsi di direktori lokal aplikasi. Pada perangkat yang di-root atau di-jailbreak, token ini dapat diekstraksi langsung melalui file system atau via Android Debug Bridge (ADB) backup jika tidak dimitigasi secara eksplisit.
Anatomi Kerentanan: Mengapa AsyncStorage Berbahaya
Secara arsitektur, AsyncStorage dirancang untuk menyimpan preferensi UI non-sensitif, bukan kredensial kriptografis. Terdapat tiga vektor serangan utama terhadap penyimpanan token di AsyncStorage:
- Root/Jailbreak File Access: Penyerang atau malware dengan akses root dapat langsung membaca direktori
/data/data/com.yourapp/databases/RKStoragedi Android atauLibrary/Application Supportdi iOS tanpa halangan. - ADB Backup Extraction: Secara default, atribut
android:allowBackup="true"padaAndroidManifest.xmlmemungkinkan seluruh isiAsyncStoragediekspor ke file.abtanpa memerlukan hak akses root. - Memory Dumps & Plaintext Inspection: Data dari storage dibaca langsung ke JavaScript heap tanpa proteksi memori terisolasi, memudahkan inspeksi runtime via framework instrumen seperti Frida.
Arsitektur Hardware-Backed Storage: TEE, StrongBox, dan Secure Enclave
Untuk mengamankan token secara deterministik, kunci enkripsi dan data sensitif harus dilindungi oleh modul komputasi terisolasi di tingkat silikon:
- iOS Keychain & Secure Enclave (SEP): Kunci enkripsi dibuat di dalam prosesor terisolasi (Secure Enclave) yang memiliki memori dan boot ROM terpisah dari prosesor aplikasi utama. Atribut
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnlymemastikan data dienkripsi saat perangkat terkunci dan kuncinya tidak pernah disinkronkan ke backup iCloud. - Android Keystore, TEE, dan StrongBox: Android mengisolasi materi kriptografis di dalam Trusted Execution Environment (TEE). Pada perangkat modern (Android 9+), modul StrongBox Keymaster menyediakan hardware terdedikasi (HSM independen dengan CPU, timer, dan storage anti-tamper tersendiri) yang kebal terhadap eksploitasi kernel OS utama.
Implementasi Proteksi Token via react-native-keychain
Library react-native-keychain membungkus implementasi native iOS Keychain Services dan Android KeyStore API ke dalam antarmuka JavaScript yang konsisten. Instalasi pustaka:
npm install react-native-keychain
# atau
yarn add react-native-keychain1. Modul Manajemen Token (TokenStorage.ts)
Berikut adalah implementasi modul penyimpanan token dengan memanfaatkan level proteksi hardware, isolasi backup, dan fallback otomatis:
import * as Keychain from 'react-native-keychain';
const AUTH_SERVICE = 'com.yourapp.auth';
interface StoreTokenOptions {
requiresBiometrics?: boolean;
}
export const storeAuthToken = async (
token: string,
options: StoreTokenOptions = {}
): Promise<boolean> => {
try {
const accessControl = options.requiresBiometrics
? Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE
: Keychain.ACCESS_CONTROL.NONE;
const result = await Keychain.setGenericPassword('authToken', token, {
service: AUTH_SERVICE,
// iOS: Kunci tidak pernah diekspor ke iCloud Backup dan hanya dapat diakses saat device aktif
accessible: Keychain.ACCESSIBLE.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY,
accessControl,
// Android: Paksa enkripsi menggunakan StrongBox / TEE hardware isolation
securityLevel: Keychain.SECURITY_LEVEL.SECURE_HARDWARE,
});
return result !== false;
} catch (error) {
// Fallback strategi jika hardware level StrongBox tidak didukung perangkat
return await storeTokenWithFallback(token);
}
};
const storeTokenWithFallback = async (token: string): Promise<boolean> => {
try {
const fallbackResult = await Keychain.setGenericPassword('authToken', token, {
service: AUTH_SERVICE,
accessible: Keychain.ACCESSIBLE.AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY,
// Fallback ke software keystore jika TEE/StrongBox hardware error
securityLevel: Keychain.SECURITY_LEVEL.SECURE_SOFTWARE,
});
return fallbackResult !== false;
} catch (error) {
console.error('Gagal menyimpan token ke Keystore:', error);
return false;
}
};
export const getAuthToken = async (): Promise<string | null> => {
try {
const credentials = await Keychain.getGenericPassword({
service: AUTH_SERVICE,
});
if (credentials && credentials.password) {
return credentials.password;
}
return null;
} catch (error) {
console.error('Gagal mendekripsi token dari Keystore:', error);
return null;
}
};
export const clearAuthToken = async (): Promise<boolean> => {
try {
return await Keychain.resetGenericPassword({ service: AUTH_SERVICE });
} catch (error) {
console.error('Gagal menghapus token dari Keystore:', error);
return false;
}
};Strategi Fallback dan Keamanan Bertingkat
Meskipun arsitektur StrongBox menawarkan proteksi terbaik, implementasi di lapangan harus memperhitungkan variasi fragmentasi Android:
- Level 1 (Ideal):
SECURITY_LEVEL.SECURE_HARDWARE(StrongBox HSM terdedikasi atau ARM TrustZone TEE). Mencegah dump memory secara fisik maupun via eksploitasi root. - Level 2 (Fallback):
SECURITY_LEVEL.SECURE_SOFTWARE. Tetap mengenkripsi secret menggunakan master key Keystore di level OS, meski isolasi fisik tidak tersedia pada chipset low-end atau emulator testing. - Deteksi Kegagalan: Selalu tangkap pengecualian
KeyStoreSafeModeExceptionatau kegagalan inisialisasi kriptografi untuk mengalihkan ke fallback yang terkontrol, bukan membuat aplikasi crash.
Checklist Hardening Pelengkap
Penggunaan hardware Keystore harus didukung dengan hardening di level platform OS agar celah bypass sisi klien tertutup sepenuhnya.
1. Nonaktifkan OS Auto Backup
Cegah Android menyalin state lokal aplikasi ke Google Drive atau via koneksi debug USB (ADB). Pada android/app/src/main/AndroidManifest.xml:
<application
android:name=".MainApplication"
android:allowBackup="false"
android:fullBackupContent="false"
... >2. Blokir Screen Capture dan App Switcher Leak (FLAG_SECURE)
Auth token yang ditampilkan di layar atau state UI yang menyimpan token sementara dapat ter-expose melalui screenshot atau task snapshot saat aplikasi masuk background. Di Android, aktifkan FLAG_SECURE di dalam MainActivity.java atau MainActivity.kt:
import android.os.Bundle;
import android.view.WindowManager;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
);
}
}3. Pembersihan Memori Komprehensif saat Logout (Secure Wipe)
Menghapus token di Keystore tidak serta-merta membersihkan state yang tersisa di memory space JavaScript engine (Hermes/JSC). Lakukan sanitasi menyeluruh:
- Panggil
Keychain.resetGenericPassword()untuk menghapus representasi fisik di native storage. - Hapus cache in-memory API client (misal: Axios interceptor headers, instance Apollo Client, atau cache RTK-Query).
- Reset global root state (Redux/Zustand) ke initial state untuk memastikan token tidak tertinggal di RAM heap.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!