Menghilangkan kerentanan keamanan memori dari sumbernya
(security.googleblog.com)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
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
Selain itu, nilai bahasa dan alat yang dapat berintegrasi secara kokoh dengan kode legacy yang tidak aman juga makin besar
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
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
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
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
Tentu saja tingkat kepraktisannya sangat bervariasi di tiap tempat, tetapi ini adalah pendekatan yang seharusnya lebih umum daripada sekarang
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
Perusahaan saya sekarang juga contoh seperti itu. Kode awal ditulis para pendiri, sementara sebagian kode baru ditulis kontraktor dengan jadwal ketat
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
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
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
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
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
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
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
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
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
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++
Saya tidak menemukan angka penggunaan bahasa untuk versi Windows terbaru, tetapi Microsoft menggunakan Rust baik untuk pengembangan baru maupun penulisan ulang fitur yang sudah ada [1] [2]
[0] Lihat bagian “Evolution of the programming languages” https://blog.timac.org/2023/1128-state-of-appkit-catalyst-sw...
[1] https://www.theregister.com/2023/04/27/microsoft_windows_rus...
[2] https://www.theregister.com/2024/01/31/microsoft_seeks_rust_...
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