5 poin oleh GN⁺ 2024-09-27 | 1 komentar | Bagikan ke WhatsApp

Menghilangkan akar kerentanan keamanan memori

Hasil yang tampak paradoks

  • Ketika codebase yang ditulis dalam bahasa yang tidak aman terhadap memori terus bertambah, memindahkan pengembangan fitur baru ke bahasa yang aman terhadap memori dapat secara signifikan mengurangi kerentanan keamanan memori
  • Ini karena kerentanan berkurang secara eksponensial seiring waktu

Penjelasan matematis

  • Umur kerentanan mengikuti distribusi eksponensial
  • Kerentanan terutama muncul pada kode baru, dan seiring waktu kode menjadi lebih aman
  • Kepadatan kerentanan pada kode berusia 5 tahun 3,4 hingga 7,4 kali lebih rendah dibandingkan kode baru

Kasus nyata di Android

  • Sejak 2019, tim Android mulai mengalihkan pengembangan baru ke bahasa yang aman terhadap memori
  • Pada 2024, kerentanan keamanan memori turun dari 76% menjadi 24%
  • Seiring menurunnya kerentanan keamanan memori, risiko keamanan secara keseluruhan juga menurun

Evolusi strategi keamanan memori

  • Generasi 1: patch reaktif - pendekatan menemukan lalu memperbaiki kerentanan
  • Generasi 2: mitigasi proaktif - pendekatan yang mempersulit eksploitasi kerentanan
  • Generasi 3: penemuan kerentanan proaktif - pendekatan menemukan kerentanan lebih awal
  • Generasi 4: pencegahan dengan keyakinan tinggi - pendekatan mencegah terjadinya kerentanan dengan beralih ke bahasa yang aman terhadap memori

Keunggulan pencegahan dengan keyakinan tinggi

  • Memutus persaingan tanpa akhir antara pihak bertahan dan penyerang
  • Meningkatkan keamanan dan menurunkan biaya melalui bahasa yang aman terhadap memori
  • Meningkatkan ketepatan kode dan produktivitas pengembang

Dari pelajaran ke praktik

  • Tidak perlu membuang atau menulis ulang seluruh kode lama yang tidak aman terhadap memori
  • Mempercepat transisi ke bahasa yang aman terhadap memori dengan meningkatkan interoperabilitas
  • Mengembangkan alat untuk meningkatkan interoperabilitas antara Rust dan C++, serta Rust dan Kotlin

Peran generasi sebelumnya

  • Penggunaan mitigasi dan deteksi proaktif secara selektif
  • Saat beralih ke kode yang aman terhadap memori, kebutuhan akan mitigasi dan deteksi berkurang

Kesimpulan

  • Menggunakan bahasa yang aman terhadap memori untuk kode baru membuat kerentanan menurun secara eksponensial
  • Lebih dari 6 tahun hasil yang konsisten di Android membuktikan efektivitas pendekatan ini

Ringkasan GN⁺

  • Beralih ke bahasa yang aman terhadap memori penting untuk mengurangi kerentanan keamanan memori
  • Dari kasus tim Android, terlihat bahwa kerentanan keamanan memori menurun secara signifikan
  • Meningkatkan interoperabilitas lebih praktis daripada menulis ulang seluruh kode yang ada
  • Menggunakan bahasa yang aman terhadap memori seperti Rust dapat meningkatkan keamanan dan produktivitas sekaligus

1 komentar

 
GN⁺ 2024-09-27
Opini Hacker News
  • Tulisan yang menarik. Intinya adalah tidak perlu menulis ulang seluruh dunia
    Memindahkan pengembangan baru ke bahasa yang aman memori saja sudah bisa menghasilkan perbaikan yang berarti, dan jauh lebih mudah serta murah daripada mem-porting semuanya demi melihat dampaknya

    • Hasil ini menunjukkan bahwa, dari sisi keamanan, manfaat penulisan ulang total terbatas. Strategi mempertahankan kode legacy yang sudah matang dan memakai bahasa aman memori hanya untuk kode baru menjadi lebih efektif dari sisi biaya
      Selain itu, nilai bahasa dan alat yang dapat berintegrasi secara kokoh dengan kode legacy yang tidak aman juga makin besar
    • Penulisan ulang terlalu mahal sehingga pasti sangat jarang dilakukan, tetapi pendekatan menggerogoti secara bertahap dari pinggir sangat efektif
      Saya juga penasaran apakah ada efek semacam “kurva bak mandi” pada kode yang sangat tua. Saya masih ingat banyak orang melihat kode dan terkejut saat ada kerentanan serius OpenSSL, mungkin saat Heartbleed
    • Dari sudut pandang keamanan saya setuju, tetapi jika ingin menghilangkan garbage collection atau mengurangi penggunaan sumber daya secara keseluruhan, ceritanya bisa berbeda
    • Fakta bahwa menulis semua kode baru dengan bahasa aman memori akan mengurangi kerentanan keamanan memori bukanlah hal yang mengejutkan. Sebab kemungkinan kode baru menciptakan masalah keamanan memori baru lebih tinggi daripada kemungkinan menemukan masalah lama di kode lama
      Namun, cukup mengesankan bahwa dari 2019 hingga 2023, tanpa penulisan ulang, hanya dengan menambahkan kode baru dalam bahasa aman memori—mungkin sebagian besar Rust dan sebagian Kotlin—masalahnya turun hampir 60%. Saya juga penasaran mengapa terjadi periode stagnasi antara 2021 dan 2023
      Penggunaan ekstrapolasi angka 2024 tampak seperti upaya untuk menampilkan angka yang bagus; akan lebih bermanfaat jika mereka menganalisis dan menjelaskan mengapa ada kenaikan dari 2022 ke 2023
      Sayang juga tidak ada penjelasan berapa banyak kode aman memori baru ditulis dalam bahasa apa. Sepertinya mereka memakai Rust dan Kotlin, tetapi saya penasaran apakah Rust 95% atau 50%, dan jika porsi Kotlin besar, di area mana Kotlin dipakai alih-alih Rust
  • Grafik dalam tulisan ini menonjol karena jelas dan ringkas. Ini menunjukkan dengan baik bahwa pemilihan data dan pelabelan yang cermat dapat menyisipkan gagasan yang dimaksud secara alami ke dalam prosa
    Kesimpulan dari fakta bahwa kerentanan menurun secara eksponensial adalah bahwa fokus harus diberikan pada kode yang benar-benar baru. Menghabiskan sumber daya untuk proyek besar-besaran “tulis ulang dalam Rust” secara membabi buta tidak efisien, bahkan untuk tujuan keamanan memori maksimum
    Cukup menarik bahwa strategi paling mudah sekaligus strategi yang direkomendasikan para praktisi Rust yang pragmatis ternyata juga, berdasarkan data, merupakan strategi terbaik untuk meminimalkan kerentanan memori
    Bagian “tim Android mengamati bahwa tingkat rollback perubahan Rust kurang dari separuh C++” mengejutkan

  • “Kerentanan menurun secara eksponensial. Ada waktu paruhnya. [...] Studi skala besar tentang masa hidup kerentanan yang dipresentasikan di Usenix Security 2022 juga mengonfirmasi fenomena ini. Para peneliti menemukan bahwa mayoritas kerentanan berada pada kode baru atau kode yang baru-baru ini dimodifikasi”
    Jika begitu, untuk keamanan mungkin lebih baik berhenti menambahkan fitur baru yang tidak benar-benar diperlukan. Windows LTSC mungkin adalah versi Windows yang paling aman

    • Pada perangkat lunak yang dipelihara, bug individual yang muncul dalam operasi normal memang berkurang seiring waktu. Jika bug menimbulkan masalah, seseorang akan melaporkannya, dan sebagian dari bug itu akan diperbaiki. Inilah mekanisme penurunannya
      Namun, untuk kerentanan yang belum dieksploitasi, tidak ada mekanisme penurunan seperti itu. Kerentanan itu tetap ada tanpa menimbulkan keluhan pengguna atau laporan bug, lalu ditemukan dan dieksploitasi oleh musuh yang memiliki sumber daya dan motivasi yang cukup
      Kini jumlah musuh dengan tingkat kemampuan seperti itu lebih banyak daripada dulu
    • Pernyataan “kerentanan menurun secara eksponensial” mungkin berlaku bukan hanya untuk kerentanan, tetapi juga untuk semua jenis bug. Saya melihat fenomena seperti itu juga dalam pengujian SBCL, proyek perangkat lunak bebas yang saya ikuti. Bug baru cenderung muncul di bagian yang baru-baru ini diubah
      Tentu saja bukan berarti semua bug hanya ada di kode terbaru. Kita semua pasti pernah melihat bug yang tetap tidak terdeteksi selama bertahun-tahun. Untuk bug seperti itu, yang harus ditanyakan adalah mengapa pengujian melewatkannya
      Karena itu, pengujian harus berfokus pada kode yang baru-baru ini diubah. Khususnya mutation testing dapat diterapkan dengan sangat terfokus pada kode yang berubah atau kode yang sangat erat terkait dengannya, sehingga beban pengujian ini dapat dikurangi secara besar
      Di Google pernah ada sistem yang menggunakan mutation testing dengan cara seperti ini bersama code review
    • Meski fitur baru tidak benar-benar diperlukan untuk menjual perangkat lunak, perangkat keras baru, algoritma keamanan yang lebih baik, dan penghentian total algoritma lama akan terus terjadi. Pada akhirnya kode baru akan masuk
    • Pendekatan lain juga memungkinkan: mengompilasi hanya subset fitur yang secara eksplisit diperlukan
      Tentu saja tingkat kepraktisannya sangat bervariasi di tiap tempat, tetapi ini adalah pendekatan yang seharusnya lebih umum daripada sekarang
    • Karena itu saya menyarankan kebanyakan orang untuk tidak langsung memakai point release terbaru dari bahasa atau pustaka
      Versi bleeding-edge memiliki banyak kerentanan baru. Umumnya, rilis tertua yang masih didukung biasanya yang paling aman
      Tentu saja ada pengecualian jika fitur di versi baru memberi nilai tambah, tetapi secara umum saya pikir sebaiknya menghindari versi yang berakhiran “.0”
  • Ada korelasi antara kode baru dan kerentanan memori. Tulisan blog itu juga menawarkan kemungkinan penjelasan bahwa kerentanan memiliki waktu paruh yang cepat menurun. Namun, saya tidak mengerti mengapa kedua faktor itu ditandai memiliki hubungan sebab-akibat
    Ada beberapa penjelasan masuk akal untuk korelasi ini. Kode baru sering terkait dengan fitur baru, dan orang cenderung berfokus mencari kerentanan pada fitur baru. Selain itu, kode lama sudah lebih banyak dipakai di dunia nyata dan mungkin telah lebih banyak mengeksekusi kondisi batas tempat kerentanan memori bersembunyi
    Saya kurang nyaman mengatakan bahwa kode baru menyebabkan kerentanan memori dan bahwa kerentanan memiliki waktu paruh yang cepat menurun. Dari jumlah mentah mungkin bisa begitu, tetapi jika memikirkan kerentanan open source berdampak tinggi seperti Heartbleed atau bug invalidasi cache CPU, dari sisi dampak tampaknya tidak tepat

    • Bisa jadi kode paling lama ditulis oleh orang-orang terbaik, dengan tekanan waktu yang lebih kecil
      Perusahaan saya sekarang juga contoh seperti itu. Kode awal ditulis para pendiri, sementara sebagian kode baru ditulis kontraktor dengan jadwal ketat
    • Artikel ini mengambil data menarik dari proyek dan bahasa yang sangat spesifik dan tidak biasa, serta budaya yang sangat spesifik dan tidak biasa, lalu menggeneralisasikannya dengan kuat menjadi angka-angka pasti untuk semua kode
      Jika kesimpulan seperti “misalnya, berdasarkan masa hidup rata-rata kerentanan, kode berusia 5 tahun memiliki kepadatan kerentanan 3,4 kali lebih rendah daripada kode baru, dan memakai masa hidup yang diamati di Android dan Chromium, 7,4 kali lebih rendah” itu benar, apakah artinya kita bisa menulis kode C yang penuh cacat lalu menyimpannya offline selama 5 tahun agar menjadi aman?
      Studi ini punya data penting dan mungkin ada kebenaran di balik hal yang dibagikan, tetapi rasa percaya diri yang kurang berdasar dan perluasan kesimpulan yang berlebihan sangat mengganggu
  • “Peningkatan produktivitas: coding yang aman menggeser penemuan bug lebih ke kiri, sebelum code check-in, sehingga meningkatkan ketepatan kode dan produktivitas developer. Perubahan ini terlihat pada metrik tingkat rollback, seperti pengembalian kode darurat akibat bug tak terduga”
    “Tim Android mengamati bahwa tingkat rollback perubahan Rust kurang dari setengah C++”
    Selama 20 tahun saya menulis kode produksi berskala besar dalam berbagai bahasa, tetapi ketika menemukan Rust pada 2016, saya tahu inilah bahasa yang akan saya tekuni. Saya juga langsung membeli buku Klabnik dan Carol hari itu, dan masih punya edisi cetaknya
    Jujur saja, bahasa itu membangkitkan kembali kecintaan saya pada pemrograman

    • Masuk akal, karena alasan terbesar commit C++ saya harus di-rollback adalah crash akibat saya bodoh lupa mengecek null pointer. Jika Rust mencegah masalah itu dan masalah coding konyol serupa, memang pasti ada satu kelas rollback yang hilang sepenuhnya
  • Artikel ini menyebut “memory-safe languages (MSL)” dalam bentuk jamak, tetapi satu-satunya bahasa yang secara eksplisit disebut sebagai target migrasi dan yang interoperabilitasnya sedang ditingkatkan adalah Rust
    Kotlin juga disebut dalam konteks peningkatan interoperabilitas Rust<>Kotlin, dan memang punya beberapa fitur memory safety, tetapi saya tidak tahu apakah setingkat dengan Rust. Saya penasaran apakah Google hanya memakai dua bahasa ini, atau juga merujuk ke bahasa lain

    • Orang-orang yang peduli pada masalah ini, khususnya dalam beberapa tahun terakhir, cenderung menyukai kerangka “bahasa memory-safe” vs “bahasa yang tidak memory-safe”
      Karena akar masalahnya pada dasarnya adalah apakah bahasa itu aman secara default atau tidak. Ungkapan ini berusaha berfokus pada akar penyebab, bukan menunjuk atau merekomendasikan bahasa tertentu
      Untuk Android, yang menjadi topik artikel ini, saya tidak begitu tahu adanya upaya pindah ke bahasa memory-safe lain selain dua bahasa itu. Saya tidak mengikuti seluruh perkembangan Android, tetapi cukup sering membaca artikel seperti ini, dan saya tidak ingat ada pembahasan selain Rust atau Kotlin
    • Ini bukan hanya soal Rust. Menulis ulang layanan jaringan berbasis C ke Java, Python, atau Go juga merupakan contoh beralih ke bahasa memory-safe
      Intinya adalah kode sendiri tidak terekspos pada bug memory safety. Jika tidak benar-benar perlu, mungkin lebih baik memilih bahasa yang tidak memiliki manajemen memori manual ala Rust
    • Android pernah membahas bahasa memory-safe yang digunakan secara lebih rinci dalam tulisan blog sebelumnya: https://security.googleblog.com/2022/12/memory-safe-language...
      Google juga memublikasikan sudut pandangnya tentang memory safety di https://security.googleblog.com/2024/03/secure-by-design-goo..., yang juga membahas bahasa memory-safe yang mereka gunakan seperti Java, Go, dan Rust
    • Ada banyak bahasa memory-safe, dan Google juga memakai berbagai bahasa seperti Rust, Python, Java, dan Go. Namun kode low-level Android secara historis memakai C++, dan untuk area itu, pengganti utama yang memory-safe untuk hal baru adalah Rust
    • Java dan Kotlin dipakai untuk aplikasi, sementara Rust dipakai untuk software sistem baru
      Di seluruh Google, Go dipakai untuk sebagian software sistem, tetapi saya belum pernah melihatnya dipakai di Android
  • Jika masa hidup kerentanan mengikuti distribusi eksponensial, logikanya berfokus pada default yang aman seperti memory safety pada kode baru bernilai sangat tidak proporsional, baik secara teori maupun berdasarkan data 6 tahun codebase Android
    Mengejutkan. Ini pertama kalinya saya melihat argumen seperti ini dipakai untuk mendukung guardrail aman yang menggeser keamanan ke kiri, dan itu bagus. Terutama berguna untuk codebase legacy besar, yang orang bisa bilang “di kode legacy C++ 100 juta baris, kita tidak akan mendapat manfaat memory safety, jadi untuk apa?”
    Ini juga tampaknya berarti deteksi kerentanan ringan pun bisa memberi manfaat yang tidak proporsional besar. Bahkan jika yang dilihat hanya kode baru dan dependensi baru, bukan backlog

  • Kesimpulan yang ditarik di sini agak membuat tidak nyaman. Sanggahan yang jelas—bahwa bisa saja kita menemukan lebih sedikit kerentanan karena kita tidak menelaah kode lama dengan sama giatnya—tidak dibahas
    Jauh lebih umum melihat log commit terbaru daripada menelaah library yang tidak berubah selama 20 tahun

    • Dulu pun kita tidak menelaah kode lama sekeras itu, dan saya tidak melihat hal itu berubah
      Artikel ini tidak punya teori tentang mengapa kode lama memiliki lebih sedikit bug, tetapi menurut saya alasannya sederhana: karena bug-bug itu sudah ditemukan
      Jika kita mengasumsikan bahwa kode apa pun memiliki jumlah bug tak dikenal yang tetap per 1000 baris, maka seiring waktu jumlah eksekusi di produksi dengan berbagai input akan bertambah, sehingga peluang ditemukannya bug meningkat. Melalui perbaikan dan code review dalam prosesnya, kita dapat berharap kualitasnya rata-rata menjadi lebih baik
      Jadi seiring waktu, jumlah bug per 1000 baris pada kode yang ada akan menurun. Dengan kata lain, kode itu sudah melalui validasi di dunia nyata
      Seperti yang dikatakan artikel, jika bug baru terus diperkenalkan dengan laju yang sama, tidak akan ada kemajuan. Namun jika bahasa yang memory-safe membuat fitur baru memperkenalkan lebih sedikit bug, maka seiring waktu jumlah total bug akan berkurang
    • Saya kurang memahami argumen ini. Objek yang ditinjau adalah Android, dan orang-orang mencari kerentanan secara manual maupun otomatis berdasarkan source code dan binary, bukan berdasarkan log commit. Saya tidak tahu mengapa log commit relevan untuk menemukan bug
      Commit hanya dipakai untuk atribusi. Jika sebuah library lama yang tidak berubah selama 20 tahun telah melewati 20 tahun fuzzing dan pemeriksaan kode manual, besar kemungkinan library itu cukup kokoh
    • Saya tidak sepenuhnya puas dengan kesimpulan bahwa kode lama memiliki lebih sedikit kerentanan. Selain umur, mungkin ada faktor lain yang menjelaskan perbedaannya
      Misalnya, selama beberapa tahun terakhir para engineer mungkin berfokus menulis ulang bagian paling berisiko dengan bahasa yang memory-safe, sementara kode lama yang risikonya rendah lebih jarang diubah
      Atau perubahan proses maupun personel bisa saja menyebabkan peningkatan cacat
      Meski begitu, penjelasan bahwa setiap bug memiliki probabilitas ditemukan per satuan waktu, dan bahwa cacat yang tersisa berkurang seiring waktu, terdengar masuk akal. Jika maintainer memperbaiki lebih banyak kerentanan daripada yang baru mereka perkenalkan, kemungkinan besar kode lama memang memiliki lebih sedikit kerentanan, dan yang tersisa lebih sulit ditemukan
    • Yang menjadi perhatian bukan kerentanan teoretis, melainkan kerentanan yang benar-benar dieksploitasi. Jika penyerang tidak mencoba mencari kerentanan pada suatu kode, secara praktis kerentanan itu seperti tidak ada
  • Saya penasaran bagaimana logika ini berlaku untuk Mac dan Windows, mengingat kode Mac baru sebagian besar ditulis dalam Swift yang memory-safe, sementara Windows masih terutama memakai C atau C++

  • Jika memikirkan fase akhirnya di sini, semakin langka kerentanan, semakin bernilai ia. Kerentanan yang tersisa akan disimpan rapat-rapat oleh aktor negara dan digunakan dengan hemat untuk target bernilai tinggi
    Jika tulisan blog ini menjelaskan generasi ke-4, generasi ke-5 mungkin mirip Lockdown Mode di iOS. Pengguna yang mengkhawatirkan keamanan dapat menyalakan checkbox untuk meningkatkan keamanan dengan menerima penurunan performa
    Checkbox idealnya mendeteksi dan menangkap serangan melalui cara seperti virtualisasi, lalu mengirimkannya untuk dianalisis tim keamanan. Ini menciptakan efek deterrence bagi penyerang. Mereka tidak ingin membakar kerentanan yang langka ketika ada kemungkinan pengguna menyalakan checkbox keamanan itu, dan banyak target bernilai tinggi akan menyalakannya
    Ini adalah herd immunity untuk kerentanan perangkat lunak, bukan patogen biologis
    Pengguna yang sadar keamanan kemungkinan juga sadar privasi. Karena itu, alih-alih diam-diam mengirim semua aktivitas pengguna, sistem harus menampilkan notifikasi kepada pengguna saat serangan terdeteksi. Menampilkan beberapa KB aktivitas jaringan yang tidak normal mungkin cukup bagi tim keamanan untuk merekonstruksi serangan, dan pengguna bisa diminta menyetujui sebelum membagikannya