Kaynaktaki bellek güvenliği açıklarını ortadan kaldırmak
(security.googleblog.com)Bellek güvenliği açıklarının kökenini ortadan kaldırmak
Paradoksal sonuç
- Bellek açısından güvenli olmayan bir dilde yazılmış kod tabanı büyürken, yeni özelliklerin bellek güvenli dillerle geliştirilmesi bellek güvenliği açıklarını büyük ölçüde azaltır
- Bunun nedeni, açıkların zaman içinde üstel olarak azalmasıdır
Matematiksel açıklama
- Açıkların yaşam süresi üstel dağılımı izler
- Açıklar ağırlıklı olarak yeni kodda ortaya çıkar ve zaman geçtikçe kod daha güvenli hale gelir
- 5 yıllık kodun açık yoğunluğu, yeni koda göre 3,4 ila 7,4 kat daha düşüktür
Android'deki gerçek örnek
- Android ekibi 2019'dan itibaren yeni geliştirmeleri bellek güvenli dillere taşımaya başladı
- 2024 itibarıyla bellek güvenliği açıklarının oranı %76'dan %24'e düştü
- Bellek güvenliği açıkları azaldıkça genel güvenlik riski de azaldı
Bellek güvenliği stratejisinin evrimi
-
- nesil: tepkisel yama — açıkları bulup düzeltme yaklaşımı
-
- nesil: proaktif azaltım — açıkların istismar edilmesini zorlaştırma yaklaşımı
-
- nesil: proaktif açık keşfi — açıkları önceden bulma yaklaşımı
-
- nesil: yüksek güvenli önleme — bellek güvenli dillere geçerek açıkların en baştan oluşmasını önleme yaklaşımı
Yüksek güvenli önlemenin avantajları
- Savunmacılar ile saldırganlar arasındaki bitmeyen yarışı keser
- Bellek güvenli dillerle güvenliği artırır ve maliyeti düşürür
- Kodun doğruluğunu ve geliştirici üretkenliğini artırır
Dersten uygulamaya
- Mevcut bellek açısından güvenli olmayan kodun tamamını atmak veya yeniden yazmak gerekmez
- Birlikte çalışabilirliği geliştirerek bellek güvenli dillere geçiş hızlandırılabilir
- Rust ile C++ ve Rust ile Kotlin arasında birlikte çalışabilirliği iyileştiren araçlar geliştiriliyor
Önceki nesillerin rolü
- Proaktif azaltım ve tespitin seçici kullanımı
- Bellek güvenli koda geçildikçe azaltım ve tespit ihtiyacı da azalır
Sonuç
- Yeni kodda bellek güvenli diller kullanmak, açıkların üstel olarak azalmasını sağlar
- Android'de 6 yılı aşkın süredir görülen tutarlı sonuçlar, bu yaklaşımın etkili olduğunu kanıtlıyor
GN⁺ özeti
- Bellek güvenliği açıklarını azaltmak için bellek güvenli dillere geçiş önemlidir
- Android ekibinin örneği, bellek güvenliği açıklarında büyük bir düşüş olduğunu gösteriyor
- Mevcut kodu tamamen yeniden yazmak yerine birlikte çalışabilirliği geliştirmek daha pratiktir
- Rust gibi bellek güvenli diller kullanmak, güvenliği ve üretkenliği aynı anda artırabilir
1 yorum
Hacker News yorumları
İlginç bir yazı. Esas nokta, tüm dünyayı baştan yazmaya gerek olmadığı
Yeni geliştirmeyi bellek güvenli dillere taşımak bile anlamlı bir iyileşme sağlayabiliyor; etkisini görmek için her şeyi port etmekten çok daha kolay ve ucuz
Ayrıca güvenli olmayan eski kodla sağlam biçimde entegre olabilen dillerin ve araçların değeri de artıyor
Çok eski kodlarda “küvet eğrisi” gibi bir etkinin olup olmadığını da merak ediyorum. Ciddi OpenSSL açıkları, muhtemelen Heartbleed zamanında, birçok kişinin koda bakıp dehşete düştüğü hâlâ aklımda
Ancak 2019’dan 2023’e kadar, yeniden yazmadan yalnızca yeni kodu bellek güvenli bir dille, muhtemelen çoğunlukla Rust ve bir miktar Kotlin ile ekleyerek sorunların neredeyse %60 azalması etkileyici. 2021 ile 2023 arasındaki duraklama döneminin neden oluştuğunu da merak ediyorum
2024 sayılarının ekstrapolasyonla kullanılması, iyi görünen rakamlar gösterme niyetini belli ediyor; bunun yerine 2022’den 2023’e neden artış olduğunu analiz edip açıklasaydı daha yararlı olurdu
Yeni bellek güvenli kodun hangi dillerle ve ne kadar yazıldığı da eksik, bu da üzücü. Rust ve Kotlin’in ikisini de kullanıyorlar gibi görünüyor; Rust %95 mi, %50 mi, Kotlin’in payı büyükse Rust yerine hangi alanlarda kullanılıyor merak ediyorum
Bu yazıdaki grafikler net ve öz oluşlarıyla öne çıkıyor. Dikkatli veri seçimi ve etiketlemenin, amaçlanan düşünceyi düzyazının içine doğal biçimde yerleştirebildiğini iyi gösteriyor
Açıkların üstel olarak azaldığı gerçeğinin sonucu, tamamen yeni koda odaklanmak gerektiği. Devasa, gelişigüzel “Rust ile yeniden yazma” projelerine kaynak harcamak, maksimum bellek güvenliği hedefi için bile verimsiz
En kolay stratejinin ve pratik Rust uzmanlarının önerdiği stratejinin, veriye göre bellek açıklarını en aza indirmek için de en iyi strateji olması oldukça güzel örtüşüyor
“Android ekibi, Rust değişikliklerinin geri alma oranının C++’ın yarısından az olduğunu gözlemledi” kısmı şaşırtıcı
“Açıklar üstel olarak azalır. Bir yarı ömürleri vardır. [...] 2022’de Usenix Security’de sunulan büyük ölçekli bir açık ömrü araştırması da bu olguyu doğruladı. Araştırmacılar, açıkların büyük çoğunluğunun yeni kodda ya da yakın zamanda değiştirilmiş kodda bulunduğunu keşfetti”
Öyleyse güvenlik için mutlaka gerekli olmayan yeni özellikler eklemeyi durdurmak daha iyi olabilir. Windows LTSC muhtemelen Windows’un en güvenli sürümüdür
Ancak henüz istismar edilmemiş açıklar için böyle bir azalma mekanizması yoktur. Kullanıcı şikâyeti ya da hata raporu üretmeden orada kalırlar; yeterli kaynağı ve motivasyonu olan bir hasım bulup istismar edene kadar
Bu seviyede hasımlar eskisine göre daha fazla
Elbette bu, tüm hataların yalnızca yeni kodda olduğu anlamına gelmez. Yıllarca tespit edilmeden kalmış hatalar da herkesin karşısına çıkmıştır. Böyle hatalar için testlerin bunları neden kaçırdığını sormak gerekir
Dolayısıyla testler yakın zamanda değiştirilen koda odaklanmalı. Özellikle mutasyon testi, değiştirilen koda ya da onunla güçlü biçimde bağlı koda çok yoğun uygulanabildiği için bu testin yükünü ciddi biçimde azaltabilir
Google’da kod incelemesiyle birlikte mutasyon testini bu şekilde kullanan bir sistem vardı
Elbette bunun her yerde ne kadar pratik olduğu çok değişir, ama bugün olduğundan daha yaygın olması gereken bir yöntem
En uç güncel sürümlerde çok sayıda yeni açık bulunur. Genellikle desteklenen en eski sürüm çoğu zaman en güvenlisidir
Elbette yeni sürümün özellikleri değer katıyorsa bu bir istisnadır; ama genel olarak “.0” ile biten sürümlerden kaçınmanın iyi olduğunu düşünüyorum
Yeni kod ile bellek güvenliği açıkları arasında bir korelasyon var. Blog yazısı, güvenlik açıklarının hızla azalan bir yarı ömre sahip olduğu şeklinde olası bir açıklama da sunuyor. Ama iki unsur arasında neden nedensellik varmış gibi gösterildiğini anlamıyorum.
Bu korelasyon için birden fazla makul açıklama mümkün. Yeni kod çoğu zaman yeni özelliklerle ilişkili olur ve insanlar güvenlik açıklarını yeni özelliklerde aramaya odaklanır. Ayrıca eski kod gerçek kullanımda daha fazla sınanmış, bellek açıklarının saklandığı sınır koşullarını daha çok çalıştırmış olabilir.
Yeni kodun bellek açıklarına yol açtığını ve güvenlik açıklarının hızla azalan bir yarı ömrü olduğunu söylemek içime sinmiyor. Salt sayı olarak öyle olabilir, ama Heartbleed gibi açık kaynaklı, etkisi yüksek güvenlik açıklarını ya da CPU önbellek geçersizleştirme hatalarını düşününce etki açısından doğru görünmüyor.
Şu anki şirketim de buna bir örnek. İlk kodu kurucular yazdı; yeni kodun bir kısmını ise sıkışık takvimlerle çalışan sözleşmeli geliştiriciler yazıyor.
“Örneğin ortalama güvenlik açığı ömrüne göre 5 yıllık kodun açık yoğunluğu yeni koda kıyasla 3,4 kat daha düşük; Android ve Chromium’da gözlemlenen ömürler kullanıldığında ise 7,4 kat daha düşük” sonucu doğruysa, bu, kusur dolu C kodu yazıp 5 yıl çevrimdışı bekletirsek güvenli hâle geleceği anlamına mı geliyor?
Bu çalışmada önemli veriler var ve paylaşılanların altında bir doğruluk payı da olabilir, ama yetersiz dayanakla sergilenen özgüven ve aşırı genelleme çok rahatsız edici.
“Verimlilik artışı: Güvenli kodlama, hata yakalamayı kodun check-in edilmesinden önce daha sola kaydırarak kod doğruluğunu ve geliştirici verimliliğini artırır. Bu değişim, beklenmedik hatalar nedeniyle yapılan acil kod geri almaları gibi rollback oranı metriklerinde görülür.”
“Android ekibi, Rust değişikliklerinin rollback oranının C++’ın yarısından az olduğunu gözlemledi.”
20 yıldır çeşitli dillerde büyük ölçekli üretim kodu yazıyorum, ama 2016’da Rust’ı keşfettiğimde kendimi kaptıracağım dilin bu olduğunu anladım. Klabnik ve Carol’ın kitabını da aynı gün satın aldım; hâlâ basılı kopyası duruyor.
Açıkçası programlamaya olan sevgimi yeniden canlandırdı.
Yazıda “bellek güvenli diller (MSL)” çoğul olarak geçiyor, ancak geçiş hedefi ve birlikte çalışabilirliği geliştirilen dil olarak açıkça belirtilen tek dil Rust.
Kotlin de Rust<>Kotlin birlikte çalışabilirliğinin geliştirilmesi bağlamında anılıyor ve bir ölçüde bellek güvenliği özellikleri var, ama Rust ile aynı seviyede olup olmadığını bilmiyorum. Google’ın kullandıkları yalnızca bu ikisi mi, yoksa başka dilleri de mi kastediyor, merak ediyorum.
Çünkü varsayılan olarak güvenli olup olmamak sorunun kökünde yer alıyor. Belirli bir dili işaret etmek ya da önermek yerine, temel nedene odaklanmayı amaçlayan bir ifade.
Bu yazının konusu olan Android özelinde, bu ikisi dışındaki bellek güvenli dillere geçme girişimlerini pek bilmiyorum. Android geliştirmeyi genel olarak takip etmiyorum ama böyle yazıları epey izliyorum; Rust veya Kotlin dışında bir şey ele aldıklarını hatırlamıyorum.
Esas nokta, kendi kodunuzda bellek güvenliği hatalarına maruz kalmamanızdır. Mutlaka gerekmiyorsa, Rust tarzı manuel bellek yönetimi olmayan bir dil seçmek daha iyi bile olabilir.
Google da bellek güvenliğine bakışını https://security.googleblog.com/2024/03/secure-by-design-goo... adresinde yayımladı; Java, Go, Rust gibi kullandığı bellek güvenli dilleri de ele alıyor.
Google genelinde Go bazı sistem yazılımlarında kullanılıyor, ama Android’de kullanıldığını görmedim.
Güvenlik açıklarının ömrü üstel dağılım izliyorsa, yeni kodda bellek güvenliği gibi güvenli varsayılanlara odaklanmanın hem teorik olarak hem de Android kod tabanındaki 6 yıllık verilere göre orantısız derecede değerli olduğu mantığı çıkıyor.
Şaşırtıcı. Böyle bir argümanın, güvenliği sola çeken güvenlik korkuluklarını desteklemek için kullanıldığını ilk kez görüyorum ve harika. Özellikle “100 milyon satırlık C++ legacy kodda bellek güvenliğinin faydasını göremeyeceğiz, o zaman neden yapalım?” denebilecek büyük legacy kod tabanları için faydalı.
Hafif güvenlik açığı tespitinin de orantısız derecede büyük fayda sağlayabileceği anlamına geliyor gibi görünüyor. Backlog’a değil, yalnızca yeni koda ve yeni bağımlılıklara bakılsa bile durum böyle.
Buradan çıkarılan sonuç biraz rahatsız edici. Bariz bir karşı argüman, yani eski koda daha az dikkatle bakıldığı için daha az güvenlik açığı bulunuyor olabileceği ele alınmamış
20 yıldır değişmemiş bir kütüphaneye bakmaktansa yakın tarihli commit günlüğüne bakmak çok daha yaygın
Eski kodda neden daha az hata olduğuna dair yazıda bir teori yok, ama benim fikrim basit: Zaten bulunmuş olmaları
Herhangi bir kodda 1000 satır başına sabit sayıda bilinmeyen hata olduğunu varsayarsak, zaman geçtikçe üretim ortamında çeşitli girdilerle çalıştırılma sayısı birikir ve bulunma olasılığı artar. Düzeltmeler ve bu süreçteki kod incelemeleriyle ortalamada daha iyi hale gelmesini bekleyebiliriz
Bu yüzden zaman geçtikçe mevcut kodun 1000 satır başına hata sayısı azalır. Bir nevi sahada doğrulanmış olur
Yazıda söylendiği gibi yeni hataları aynı hızla sürekli eklemeye devam ederseniz ilerleme olmaz; ama bellek güvenli diller yeni özelliklerde daha az hata eklenmesini sağlıyorsa, zamanla toplam hata sayısı azalacaktır
Commit'ler yalnızca atıf için kullanılır. 20 yıldır değişmemiş eski bir kütüphane 20 yıl boyunca fuzzing'den ve manuel kod incelemesinden geçtiyse, oldukça sağlam olma ihtimali yüksektir
Örneğin son birkaç yılda mühendisler en riskli kısımları bellek güvenli dillerle yeniden yazmaya odaklanmış, daha düşük riskli eski kodu ise daha az değiştirmiş olabilir
Ya da süreç veya personel değişiklikleri kusur artışına yol açmış olabilir
Yine de her hatanın birim zaman başına bulunma olasılığı olduğu ve zaman geçtikçe kalan kusurların azaldığı açıklaması makul. Bakımcılar yeni ekledikleri güvenlik açıklarından daha fazlasını düzeltiyorsa, eski kodda güvenlik açıklarının azalması ve kalanların bulunmasının zor olması olasıdır
Yeni Mac kodlarının çoğu bellek güvenli Swift ile yazılırken Windows'un hâlâ ağırlıklı olarak C ya da C++ kullanması açısından, bu mantığın Mac ve Windows için nasıl geçerli olduğunu merak ediyorum
Güncel Windows sürümlerindeki dil kullanımına dair rakamlar bulamadım, ancak Microsoft hem yeni geliştirmelerde hem de mevcut özellikleri yeniden yazmada Rust kullanıyor [1] [2]
[0] “Evolution of the programming languages” bölümüne bakın 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_...
Burada son aşamayı düşünürsek, güvenlik açıkları nadirleştikçe daha değerli hale gelir. Kalan açıklar devlet aktörleri tarafından titizlikle stoklanacak ve yüksek değerli hedefler için saklanacaktır
Bu blog yazısı 4. nesli anlatıyorsa, 5. nesil iOS'taki Lockdown Mode'a benzer olabilir. Güvenlikten endişe duyan kullanıcıların performans düşüşünü göze alıp güvenliği artıran bir onay kutusunu açabilmesi
İdeal onay kutusu, sanallaştırma gibi yöntemlerle saldırıyı tespit edip yakalar ve ardından güvenlik ekibinin analizi için gönderir. Bu, saldırgan için caydırıcılık yaratır. Kullanıcının o güvenlik kutusunu açmış olabileceği bir durumda kıt bir güvenlik açığını harcamak istemezler ve yüksek değerli hedeflerin çoğu o kutuyu açacaktır
Biyolojik patojenlere değil, yazılım güvenlik açıklarına yönelik sürü bağışıklığı
Güvenlik bilinci yüksek kullanıcıların gizlilik bilincinin de yüksek olması muhtemel. Bu yüzden tüm kullanıcı etkinliğini sessizce göndermek yerine, bir saldırı tespit edildiğinde kullanıcıya bildirim gösterilmeli. Birkaç KB'lık anormal ağ etkinliğini göstermek, güvenlik ekibinin saldırıyı yeniden kurgulaması için yeterli olabilir ve paylaşmadan önce kullanıcının onayı alınabilir