Belajar Hermes JS Engine - Hermes and JIT / AOT Security Considerations
Episode 13 of 23

Belajar Hermes JS Engine - Hermes and JIT / AOT Security Considerations

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.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

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.

AOT vs JIT: Profil Keamanan yang Berbeda

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.

AspekEngine JIT (V8/JSC)Hermes (AOT, interpreter)
Kode mesin dibuat saat runtimeYa, saat hot path ditemukanTidak, semua sudah jadi bytecode
Permukaan serangan JITJIT spraying, bug compilerTidak ada
Determinisme eksekusiBergantung pada profil runtimeIdentik antar perangkat
Keterkaitan versiBytecode/code cache rapuhBytecode 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.

Signed Bundles dan Content Integrity

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:

Memverifikasi tanda tangan APK dari CI
apksigner verify --print-certs app-release.apk

Untuk 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:

JSVerifikasi digest via modul native tepercaya
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;
}

Integrasi dengan App Hardening

Hardening membangun lapisan pertahanan di sekitar runtime agar analisis statis dan dinamis lebih mahal. Untuk proyek React Native dengan Hermes:

  • Minify native code: aktifkan R8/Proguard di release agar nama kelas dan method Java tidak terbaca langsung dari APK.
  • Strip simbol debug: jangan sertakan tabel simbol atau binary dengan simbol dalam paket produksi.
  • Cabut inspector: Hermes release build di React Native otomatis tidak menyertakan libhermes-inspector.so — pastikan build release kalian tidak memuatnya.
  • Jangan ikutkan source map: kirim source map ke tool observability, bukan ke perangkat.
  • Kunci flag kompiler: pastikan semua developer memakai set hermesFlags yang sama supaya bytecode yang dihasilkan konsisten dan bisa diaudit.
R8 aktif plus flag Hermes terkunci
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.

Runtime Privacy

Keamanan runtime tidak ada artinya kalau aplikasi membocorkan data secara diam-diam. Beberapa praktik yang wajib jadi kebiasaan:

  • Jangan menyimpan secret di JavaScript: token, kunci API, dan password tidak boleh menjadi string literal di bundel — bundel bisa dibongkar. Tempatkan di sisi server atau di keychain/keystore melalui modul native.
  • Redaksi log: pastikan telemetry dan crash reporting memotong informasi sensitif sebelum dikirim.
  • Minimalkan heap footprint data sensitif: semakin sedikit data pengguna yang hidup di heap JavaScript, semakin kecil yang bisa diambil dari heap snapshot.
JSRedaksi data sebelum dikirim ke telemetry
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.

Penutup

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:

  • Tanpa JIT, Hermes tidak punya celah JIT spraying atau W+X — ini keuntungan arsitektural bawaan.
  • Bytecode AOT terkunci ke versi engine; selalu naikkan bytecode bersama versi Hermes saat upgrade.
  • Signed bundles membuktikan asal-usul, bukan sekadar kekekalan — pakai tanda tangan kriptografis, bukan hash statis saja.
  • Hardening (R8, strip simbol, tanpa inspector, tanpa source map) melapisi runtime di level native.
  • Secret tidak pernah boleh hidup di JavaScript; redaksi data sebelum masuk telemetry.

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!

Belajar Hermes JS Engine - Hermes and JIT / AOT Security Considerations | Belajar Hermes JS Engine