Episode ini membedah implikasi keamanan dari arsitektur AOT Hermes dibandingkan engine ber-JIT, konsep signed bundles dan content integrity, lalu cara mengintegrasikan app hardening serta runtime privacy ke dalam build produksi.

Episode 12 menutup dengan secure runtime practices: bundel dimuat dengan verifikasi, dan pola eval dinamis dijauhi. Sekarang kita perbesar kacamata. Pertanyaan besar di episode 13: apakah arsitektur kompilasi Hermes — yang Ahead-of-Time, tanpa JIT — mengubah profil keamanan secara fundamental? Jawaban singkatnya: ya, dan kebanyakan perubahannya menguntungkan kita.
Roadmap episode ini: dampak keamanan AOT versus JIT, signed bundles dan content integrity, lalu integrasi dengan app hardening serta runtime privacy.
Engine ber-JIT seperti V8 atau JavaScriptCore memiliki satu titik kompleksitas yang tidak dimiliki Hermes: mereka menulis kode mesin baru ke memori saat runtime. Halaman memori yang awalnya writable lalu diubah menjadi executable (W+X) adalah zona yang sangat menarik bagi penyerang. Teknik seperti JIT spraying — menyisipkan instruksi berbahaya ke dalam data yang kemudian diinterpretasi sebagai kode oleh JIT — menjadi mungkin persis karena mekanisme ini. Setiap bug di compiler JIT juga berpotensi jadi primitif "code execution".
Hermes sengaja tidak memiliki JIT. Kode dijalankan oleh interpreter di atas bytecode yang dibuat saat build, deterministik dan identik di semua perangkat. Tidak ada halaman memori baru yang dibuat executable saat runtime, sehingga kelas serangan berbasis JIT otomatis tertutup. Bytecode juga disimpan read-only dan di-backing oleh file, sehingga sistem operasi bisa mengevakuasi halaman tersebut saat memori menipis.
| Aspek | Engine JIT (V8/JSC) | Hermes (AOT, interpreter) |
|---|---|---|
| Kode mesin dibuat saat runtime | Ya, saat hot path ditemukan | Tidak, semua sudah jadi bytecode |
| Permukaan serangan JIT | JIT spraying, bug compiler | Tidak ada |
| Determinisme eksekusi | Bergantung pada profil runtime | Identik antar perangkat |
| Keterkaitan versi | Bytecode/code cache rapuh | Bytecode terkunci ke versi engine |
Konsekuensi dari determinisme itu: bytecode .hbc yang dikompilasi dengan hermesc versi tertentu hanya berjalan di engine Hermes versi yang kompatibel. Ini seperti kontrak format yang kaku — bermanfaat untuk keamanan (tidak ada yang bisa "menyelipkan" bytecode versi lain), tapi mengharuskan kalian selalu menaikkan bytecode bersama versi Hermes saat upgrade.
Info
Dampak keamanan lain dari AOT: karena bytecode tidak "dipanasi" saat runtime seperti JIT, waktu startup tidak memunculkan jendela di mana engine melakukan kompilasi yang bisa dieksploitasi. Yang tersisa hanyalah interpreter yang relatif sederhana dan mudah diaudit.
Menentukan "bundel ini milik kita" butuh lebih dari sekadar hash. Hash SHA-256 membuktikan bundel tidak berubah sejak dihitung, tapi tidak membuktikan siapa yang menghitungnya. Untuk itu kita butuh tanda tangan kriptografis: bundel ditandatangani dengan kunci privat di sisi build, dan aplikasi memverifikasi menggunakan kunci publik yang disematkan.
Di level aplikasi, rantai ini sudah ada: APK/IPA ditandatangani sebelum masuk toko (Android App Signing, iOS code signing). Artinya bytecode di dalamnya ikut dilindungi selama kalian tidak mengunduh bundel dari tempat lain. Kalau kalian memverifikasi tanda tangan aplikasi di sisi server — misalnya memakai Play Integrity API — kalian juga memastikan bahwa yang meminta fitur premium adalah aplikasi asli kalian, bukan versi tamper:
apksigner verify --print-certs app-release.apkUntuk bundel yang diunduh saat runtime (OTP update), prinsip content integrity yang sama berlaku di layer Hermes: hitung digest bundel di sisi build, sertakan sebagai metadata yang ditandatangani, lalu verifikasi sebelum dieksekusi. Digestsnya jangan pernah disimpan sebagai konstanta yang bisa diedit penyerang — tempatkan di native layer yang sudah dilindungi:
const Integrity = NativeModules.BundleIntegrity;
async function loadBundle(bytes) {
const ok = await Integrity.verify(bytes);
if (!ok) throw new Error("bundel tidak ditandatangani oleh issuer resmi");
return bytes;
}Hardening membangun lapisan pertahanan di sekitar runtime agar analisis statis dan dinamis lebih mahal. Untuk proyek React Native dengan Hermes:
libhermes-inspector.so — pastikan build release kalian tidak memuatnya.hermesFlags yang sama supaya bytecode yang dihasilkan konsisten dan bisa diaudit.react {
hermesFlags = ["-O", "-output-source-map"]
}
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
}
}
}Warning
minifyEnabled true hanya memengaruhi kode native Java/Kotlin — bukan bytecode Hermes. Untuk melindungi logika JavaScript, tambahkan obfuscator di sisi bundling (misalnya memakai transformer atau plugin Metro) dan pindahkan logika sensitif ke modul native.
Keamanan runtime tidak ada artinya kalau aplikasi membocorkan data secara diam-diam. Beberapa praktik yang wajib jadi kebiasaan:
function redactUserData(user) {
return {
id: user.id,
role: user.role,
email: mask(user.email),
};
}
function mask(value) {
const at = value.indexOf("@");
return at === -1 ? "***" : `***${value.slice(at)}`;
}Prinsipnya: kode aplikasi boleh kompleks, tetapi data pribadi harus selalu diperlakukan sebagai barang yang perlu dihitung jejaknya — siapa yang bisa mengaksesnya, ke mana ia pergi, dan berapa lama ia hidup.
Pilihan AOT tanpa JIT membuat Hermes berada di posisi yang menarik secara keamanan: permukaan serangan kelas JIT hilang, dan bytecode yang deterministik membuat perilaku runtime mudah diprediksi dan diaudit. Yang menjadi tanggung jawab kalian adalah melengkapi fondasi itu dengan integritas bundel, hardening build, dan disiplin privacy.
Inti yang harus dibawa pulang:
Di episode 14, kita masuk Application Architecture & Isolation: menjaga batas antara business logic dan native modules, memodularisasi bundel, dan menjalankan skrip yang tidak dipercaya dalam konteks ephemeral yang terisolasi. Sampai jumpa!