5 puan yazan GN⁺ 2024-09-27 | 1 yorum | WhatsApp'ta paylaş

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

    1. nesil: tepkisel yama — açıkları bulup düzeltme yaklaşımı
    1. nesil: proaktif azaltım — açıkların istismar edilmesini zorlaştırma yaklaşımı
    1. nesil: proaktif açık keşfi — açıkları önceden bulma yaklaşımı
    1. 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

 
GN⁺ 2024-09-27
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

    • Bu sonuç, güvenlik açısından tam yeniden yazmanın getirisinin sınırlı olduğunu düşündürüyor. Olgun eski kodu koruyup yalnızca yeni kodda bellek güvenli diller kullanma stratejisinin maliyet-etkinliği daha iyi hale geliyor
      Ayrıca güvenli olmayan eski kodla sağlam biçimde entegre olabilen dillerin ve araçların değeri de artıyor
    • Yeniden yazmak çok pahalı olduğu için ancak çok nadir yapılabilir; fakat kenarlardan başlayıp kademeli olarak kemirme yöntemi son derece etkili
      Ç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
    • Güvenlik açısından katılıyorum, ama çöp toplamayı ortadan kaldırmak ya da toplam kaynak kullanımını azaltmak istiyorsanız durum değişebilir
    • Tüm yeni kodu bellek güvenli bir dille yazmanın bellek güvenliği açıklarını azaltması başlı başına şaşırtıcı değil. Çünkü yeni kodun yeni bellek güvenliği sorunları yaratma olasılığı, eski kodda mevcut sorunları bulma olasılığından daha yüksek
      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

    • Bakımı yapılan yazılımlarda normal çalışma sırasında ortaya çıkan tekil hataların zamanla azalması doğrudur. Çünkü bir hata sorun çıkarırsa biri rapor eder, bunların bir kısmı da düzeltilir. Azalma mekanizması budur
      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
    • “Açıklar üstel olarak azalır” sözü yalnızca açıklar için değil, her tür hata için de geçerli olmalı. Katıldığım özgür yazılım projesi SBCL’nin testlerinde de bu olguyu gördüm. Yeni hatalar genellikle yakın zamanda değiştirilen kısımlarda ortaya çıkma eğiliminde
      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ı
    • Yazılım satışı için yeni özellikler şart olmasa bile, yeni donanım, daha iyi güvenlik algoritmaları ve mevcut algoritmaların tamamen terk edilmesi devam eder. Sonuçta yeni kod girer
    • Başka bir yaklaşım da mümkün: yalnızca açıkça gerekli olan özellik alt kümesini derlemek
      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
    • Bu yüzden çoğu kişiye bir dilin ya da kütüphanenin en yeni nokta sürümünü hemen kullanmamalarını öneriyorum
      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.

    • En eski kodun, daha az zaman baskısı altında, en iyi kişiler tarafından yazıldığını varsayabiliriz sanırım.
      Ş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.
    • Bu yazı, çok belirli ve sıra dışı bir projeden, dilden ve çok belirli ve sıra dışı bir kültürden ilginç veriler alıp, bunları tüm kodlar için kesin sayılarla güçlü biçimde genelliyor.
      “Ö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ı.

    • C++ commit’lerimi rollback etmek zorunda kalmamın en büyük nedeninin, aptalca bir şekilde null pointer kontrolünü atlamamdan kaynaklanan crash’ler olduğunu düşününce mantıklı geliyor. Rust bu sorunu ve benzer aptalca kodlama hatalarını engelliyorsa, rollback’lerin bir sınıfı bütünüyle ortadan kalkmış olur.
  • 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.

    • Bu konuyla ilgilenenler, özellikle son birkaç yıldır “bellek güvenli diller” ve “bellek güvenli olmayan diller” çerçevesini tercih ediyor.
      Çü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.
    • Bu yalnızca Rust’a özgü bir konu değil. C ile yazılmış ağ servislerini Java, Python, Go ile yeniden yazmak da bellek güvenli bir dile geçiş örneğidir.
      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.
    • Android, önceki bir blog yazısında kullandığı bellek güvenli dilleri daha ayrıntılı ele almıştı: https://security.googleblog.com/2022/12/memory-safe-language...
      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.
    • Bellek güvenli dil çok ve Google da Rust, Python, Java, Go gibi birçok dili kullanıyor. Ancak Android’in düşük seviyeli kodu tarihsel olarak C++ idi ve bu alanda yeni geliştirilen şeyler için başlıca bellek güvenli alternatif Rust.
    • Java ve Kotlin uygulamalarda kullanılıyor, Rust ise yeni sistem yazılımlarında kullanılı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 koda eskiden de o kadar dikkatle bakılmıyordu ve bunun değiştiğini düşünmüyorum
      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
    • Bu noktayı tam anlayamıyorum. İncelenen şey Android ve insanlar commit günlüklerine değil, kaynak kodu ve ikili dosyaları temel alarak manuel ve otomatik şekilde güvenlik açıkları arıyor. Hata bulmada commit günlüklerinin neden ilgili olduğunu bilmiyorum
      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
    • Eski kodda daha az güvenlik açığı olduğu yargısından tamamen tatmin olmadım. Yaş dışında farkı açıklayabilecek etkenler olabilir
      Ö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
    • İlgi alanı teorik güvenlik açıkları değil, fiilen istismar edilen güvenlik açıkları. Saldırgan bir kodda güvenlik açığı aramaya çalışmıyorsa, pratikte yok sayılı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

  • 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