Autentikasi biometrik standar pada React Native sering kali diimplementasikan secara naif: JavaScript runtime memanggil API biometrik natif, menunggu respons penyelesaian, dan memeriksa nilai boolean success === true. Pendekatan ini rentan dieksploitasi.

Penyerang bermodalkan perangkat yang telah di-root atau di-jailbreak dapat dengan mudah memotong alur logika ini menggunakan Frida, Objection, atau Xposed framework. Cukup dengan menimpa nilai balik metode natif (misalnya BiometricPrompt.AuthenticationCallback.onAuthenticationSucceeded pada Android atau LAContext.evaluatePolicy pada iOS), seluruh sistem proteksi aplikasi dapat ditembus tanpa menyentuh sensor sidik jari sama sekali.

Vulnerabilitas Logika Boolean dan Anatomi Runtime Hooking

Saat aplikasi hanya mengevaluasi status keberhasilan secara lokal di sisi klien, batasan keamanan (trust boundary) berada sepenuhnya di lingkungan yang dapat dikendalikan oleh penyerang. Script Frida berikut mengilustrasikan betapa mudahnya memotong logika autentikasi lokal standar:

// Frida script untuk bypass verifikasi boolean lokal
Java.perform(() => {
    const FingerprintManager = Java.use('android.hardware.fingerprint.FingerprintManager');
    FingerprintManager.authenticate.overload(
        'android.hardware.fingerprint.FingerprintManager$CryptoObject',
        'android.os.CancellationSignal',
        'int',
        'android.hardware.fingerprint.FingerprintManager$AuthenticationCallback',
        'android.os.Handler'
    ).implementation = function (crypto, cancel, flags, callback, handler) {
        // Bypass: langsung panggil callback onAuthenticationSucceeded tanpa proses sensor
        callback.onAuthenticationSucceeded(null);
    };
});

Ketika hook dijalankan, library React Native akan menerima sinyal berhasil dan meneruskan status sukses ke layer JavaScript. Aplikasi kemudian menganggap pengguna telah terautentikasi dan mengeksekusi aksi sensitif, seperti mengambil token sesi atau memicu transfer dana.

Solusi: Asymmetric Cryptographic Challenge-Response

Solusi standar industri untuk mencegah bypass logika lokal adalah mengikat keberhasilan biometrik dengan operasi kriptografi asimetris di dalam perangkat keras terisolasi (Android KeyStore / iOS Secure Enclave). Kunci privat (private key) di-generate di dalam modul keamanan perangkat keras dengan flag khusus: kunci hanya dapat digunakan untuk melakukan proses signing jika sensor biometrik berhasil diverifikasi secara fisik.

Alur Kerja Protokol:

  1. Registrasi: Klien men-generate pasangan kunci asimetris (misal ECDSA P-256) di Secure Enclave / KeyStore. Kunci privat tidak pernah keluar dari modul hardware. Kunci publik dikirim dan disimpan di database backend yang diasosiasikan dengan akun pengguna.
  2. Challenge Request: Saat pengguna hendak melakukan aksi sensitif atau login, klien meminta nonce (challenge acak) dari server.
  3. Hardware Signing: Klien menandatangani payload nonce + timestamp + userId menggunakan kunci privat. Operasi ini secara otomatis memicu prompt biometrik sistem. Jika pengguna gagal atau di-bypass di layer software, kunci privat tetap terkunci di level hardware dan penandatanganan payload gagal.
  4. Verifikasi Server: Klien mengirimkan signature ke server. Server memverifikasi signature menggunakan kunci publik terdaftar dan memastikan nonce belum kedaluwarsa serta belum pernah digunakan (mencegah replay attack).

Implementasi Native Klien: React Native

Untuk menghindari manipulasi memori pada library JavaScript murni, gunakan implementasi yang mengekspos API penandatanganan hardware natif, seperti react-native-biometrics atau native module kustom yang mengonfigurasi flags hardware secara ketat.

1. Inisialisasi Pasangan Kunci dengan Hardware-Binding

import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics';

const rnBiometrics = new ReactNativeBiometrics();

async function registerBiometricKey(): Promise<string | null> {
  const { biometryType } = await rnBiometrics.isSensorAvailable();
  if (!biometryType) {
    throw new Error('Sensor biometrik tidak tersedia atau dinonaktifkan.');
  }

  // Generate EC P-256 Keypair di Secure Enclave / Android KeyStore
  const { publicKey } = await rnBiometrics.createKeys();
  return publicKey;
}

2. Menandatangani Challenge dari Server

async function authenticateWithBiometrics(challengeNonce: string) {
  const payload = `challenge:${challengeNonce}`;

  const { success, signature } = await rnBiometrics.createSignature({
    promptMessage: 'Konfirmasi otorisasi biometrik',
    payload: payload,
    cancelButtonText: 'Batal',
  });

  if (!success || !signature) {
    throw new Error('Proses otentikasi biometrik gagal atau dibatalkan.');
  }

  return signature;
}

Konfigurasi Tingkat Natif: Menangkal Serangan Pendaftaran Biometrik Baru

Salah satu celah kritis pada perangkat terkompromi adalah skenario di mana penyerang menambahkan sidik jari mereka sendiri ke pengaturan OS target. Jika aplikasi tidak dikonfigurasi dengan benar, sidik jari penyerang yang baru dapat dipakai untuk membuka kunci privat yang ada.

Android: setUserAuthenticationRequired dan setInvalidatedByBiometricEnrollment

Pada sisi native Android, pastikan KeyGenParameterSpec dikonfigurasi agar secara otomatis membatalkan validitas kunci jika ada perubahan pada template biometrik perangkat:

KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
    KEY_ALIAS,
    KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
    .setDigests(KeyProperties.DIGEST_SHA256)
    .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
    .setUserAuthenticationRequired(true)
    // Kunci otomatis rusak/invalid jika ada pendaftaran biometrik baru
    .setInvalidatedByBiometricEnrollment(true);

iOS: kSecAccessControlBiometryCurrentSet

Pada iOS, flag kSecAccessControlBiometryAny memungkinkan biometrik yang baru didaftarkan tetap dapat mengakses kunci. Ganti parameter tersebut dengan kSecAccessControlBiometryCurrentSet.

SecAccessControlCreateWithFlags(
    kCFAllocatorDefault,
    kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    .biometryCurrentSet, // Invalid jika ada sidik jari / wajah baru didaftarkan
    &error
);

Catatan: Jika pengguna menambahkan jari baru ke perangkat mereka, kunci privat yang dilindungi oleh flag ini akan terhapus atau dibatalkan secara permanen oleh hardware. Aplikasi harus menangani exception ini dan mengarahkan pengguna untuk mendaftar ulang dengan kredensial utama (seperti password atau 2FA OTP).

Verifikasi Sisi Server (Backend)

Server bertindak sebagai pemegang otoritas tunggal. Jangan pernah mempercayai klien tanpa memvalidasi signature secara kriptografis terhadap payload challenge yang diterbitkan.

import crypto from 'crypto';

interface VerifyRequest {
  publicKeyBase64: string;
  signatureBase64: string;
  nonce: string;
}

export function verifyBiometricChallenge({
  publicKeyBase64,
  signatureBase64,
  nonce,
}: VerifyRequest): boolean {
  const payload = Buffer.from(`challenge:${nonce}`, 'utf8');
  const signature = Buffer.from(signatureBase64, 'base64');
  
  // Rekonstruksi public key dalam format PEM atau DER
  const publicKey = crypto.createPublicKey({
    key: Buffer.from(publicKeyBase64, 'base64'),
    format: 'der',
    type: 'spki',
  });

  const verifier = crypto.createVerify('SHA256');
  verifier.update(payload);
  verifier.end();

  // Verifikasi signature ECDSA
  return verifier.verify(publicKey, signature);
}

Menangani Siklus Hidup dan Error Boundary Hardware

Implementasi kriptografi tingkat rendah membutuhkan penanganan error yang komprehensif pada lapisan presentasi React Native:

  • KeyPermanentlyInvalidatedException: Terjadi saat biometrik baru didaftarkan di perangkat. Tangkap error ini, bersihkan kunci lokal via deleteKeys(), tandai kunci di server sebagai non-aktif, lalu paksa pengguna login ulang via kredensial fallback.
  • Hardware Lockout: Kegagalan verifikasi sensor berulang kali akan memicu BIOMETRIC_LOCKED_OUT secara natif di KeyStore. Matikan tombol biometrik di UI klien untuk sementara waktu dan tawarkan input PIN/Password aplikasi.
  • Hardware Unavailability: Kasus di mana hardware enkripsi sedang sibuk atau OS berada di status Direct Boot sebelum enkripsi disk dibuka. Selalu sediakan jalur autentikasi alternatif berbasis server.

Dengan memindahkan validasi dari status boolean lokal ke pembuktian kepemilikan kunci melalui tanda tangan kriptografis di server, teknik runtime hook Frida tidak lagi berguna karena attacker tidak dapat memalsukan signature tanpa mengekstrak private key dari hardware terisolasi.