- EEF CNA tarafından yayımlanan CVE’lerin %35,8’i kontrolsüz kaynak tüketimiyle ilgili ve BEAM ekosisteminde tekrarlayan atom tükenmesi önemli bir paya sahip
- Atom tükenmesi, hizmet reddi türünde bir zafiyettir; atom’lar garbage collection ile temizlenmez, küresel tabloda birikir ve tablo dolunca VM çöker
- Olası değer kümesinin sonlu olduğu garanti edilmeyen verilerden, örneğin kullanıcı girdisinden, atom üretmek DoS riski doğurur; URI scheme de buna dahildir
- Risk yalnızca
binary_to_atom/1,String.to_atom/1gibi açık çağrılarda değil, JSON anahtarlarını atom olarak decode etme ve string enterpolasyonuna dayalı dinamik üretimde de vardır - Güvenli yaklaşım, çalışma zamanında yeni atom üretmekten kaçınmak; bilinen değerleri açık bir lookup tablosu ya da
to_existing_atomtürevleriyle sınırlamak ve linter ile denetlemektir
Atom tükenmesinin yarattığı hizmet reddi zafiyeti
- EEF CNA tarafından yayımlanan CVE’lerin %35,8’i kontrolsüz kaynak tüketimidir ve BEAM ekosisteminde yinelenen atom tükenmesi sorunları büyük pay tutar {p:36}
- Güncel dağılım, EEF CNA’nın Common Weaknesses sayfasında görülebilir
- Atom tükenmesi bir hizmet reddi (DoS) zafiyetidir
- Atom’lar garbage collection ile temizlenmez
- Küresel atom tablosunda saklanır
- Tablo dolarsa VM çöker
- Sonlu olmayan değerlerden, özellikle kullanıcı girdisinden, atom üretmek potansiyel bir DoS yaratır
- Risk yalnızca bariz çağrılarla sınırlı değildir
- Erlang’da
binary_to_atom/1,list_to_atom/1 - Elixir’de
String.to_atom/1,List.to_atom/1
- Erlang’da
- Daha az görünür risk kalıpları da vardır
- Erlang’da enterpolasyon yoluyla dinamik atom üretimi:
% Erlang: 보간을 통한 동적 atom 생성 list_to_atom("field_" ++ UserInput) - Elixir’de JSON anahtarlarını atom olarak decode etme:
# Elixir: JSON을 atom 키로 디코딩 Jason.decode(json, keys: :atoms) - Elixir’de enterpolasyon yoluyla dinamik atom üretimi:
# Elixir: 보간을 통한 동적 atom 생성 :"field_#{user_input}"
- Erlang’da enterpolasyon yoluyla dinamik atom üretimi:
Güvenli işleme yöntemleri ve kontrol edilmesi gerekenler
- Atom tükenmesi zafiyetleri yalnızca basit bir dikkatsizlikten kaynaklanmaz; çoğu zaman girdinin denetimli ya da sonlu olduğu varsayılan kodlarda ortaya çıkar
- URI scheme bunun tipik bir örneğidir
- İşlenecek scheme sayısının yalnızca birkaç tane olduğu düşünülebilir
- Ancak değer dış girdiden geliyorsa, olası kümenin artık sonlu olduğu garanti edilemez
- Girdiden atom üreten kod, ancak olası değer kümesi sonlu, biliniyor ve zorunlu kılınıyorsa güvenlidir
- En güvenli yaklaşım, çalışma zamanında yeni atom üretmemektir
- İzin verilen değerler biliniyorsa açık bir lookup tablosu kullanmak daha güvenlidir
% Erlang case Scheme of <<"http">> -> http; <<"https">> -> https; _ -> error end - Lookup tablosu pratik değilse, yeni atom üretmeden yalnızca mevcut atom’ları kullanan varyantlar tercih edilmelidir
- Bu fonksiyonlar yeni atom oluşturmaz, hata üretir
% Erlang binary_to_existing_atom(Value) list_to_existing_atom(Value)# Elixir String.to_existing_atom(value) List.to_existing_atom(value) - Linter’lar, zafiyete dönüşmeden önce riskli kalıpları yakalamaya yardımcı olur
- Elixir projelerinde Credo’nun Credo.Check.Warning.UnsafeToAtom denetimini etkinleştirmek düşünülebilir
- Bu denetim,
String.to_atom/1,List.to_atom/1,Module.concat/1,2veJason.decode/2içindekeys: :atomskullanımının güvensiz çağrılarını işaretler - Bu denetim varsayılan olarak devre dışıdır
- Erlang veya Elixir projelerinin bakımını yapanlar; binary, string, JSON anahtarları, URI bileşenleri, header’lar ve yapılandırma değerlerinden atom üreten kodları aramalıdır
- Bu zafiyet sınıfı, CVE’ye dönüşmeden önce düzeltilmesi en kolay türlerden biridir
- Daha ayrıntılı yönergeler, EEF Security Working Group’un atom tükenmesini önleme kılavuzunda yer alıyor
1 yorum
Lobste.rs yorumları
Ruby’de
Symbolçöp toplanabilir hale gelmeden önceki duruma benziyorBaşlığı anlamadım. Bu kesinlikle bir footgun gibi görünüyor
“Ruby’de de Erlang atomlarına benzeyen symbol’ler yok muydu?” diye düşünürseniz, evet ama Ruby symbol’leri çöp topluyor
Ayrıca Erlang atomlarının tutulduğu arama tablosu varsayılan olarak en fazla 1.048.576 öğeye izin veriyor
Form gibi kullanıcı girdilerinden dinamik olarak atom üretirseniz bu çok tehlikelidir ve yazılımı hizmet engelleme saldırılarına açık hale getirir
Yine de deneyimime göre “footgun” zaten oldukça geniş bir ifade, bu yüzden başlık her hâlükârda tuhaf duruyor
Tasarımın ya da uygulamanın temelden kötü bir yanı varmış gibi gelmesi şaşırtıcı. İnternette sürekli övülen bir dil olduğu için daha da beklenmedik
Bu tabloya referans sayımı eklemek maliyetlidir ve onlarca yıldır var olan kodun ölçeklenme özelliklerini değiştirir
Varsayılan atom üst sınırı 1 milyondur ve VM başlatılırken belirlenir
Bu bir tuzaktır ama kaçınılması zor değildir. Çok uzun zamandır tavsiye edilen şey “kullanıcı girdisinden atom oluşturmayın” olmuştur
Örneğin JSON ayrıştırıyorsanız, normalde anahtarları atomlara çevirmemeli ya da yalnızca zaten var olan bir atomsa çevirmelisiniz. Böylece atom anahtarlarıyla pattern matching yapabilirsiniz; kod yüklenirken bu atomlar zaten oluşturulmuştur ve catch-all klozu atom yerine string alabilir
Elixir çok daha yaygın; dolayısıyla Erlang geliştiricileri bunu biliyor olabilir ama Elixir geliştiricileri bilmiyor olabilir
Bana göre atomları bu şekilde kullanmak baştan garip geliyor. Çünkü Erlang atomlarını kabaca C’deki enum türü gibi düşünüyorum
Belirli bir biçimde kelime yazınca içeride enum’a dönüşen bir kolaylık özelliği gibi
Yazı kullanıcı girdisinden bahsediyor ama zaten kullanıcı girdisinden yeni bir enum türü üretmek isteyeceğiniz bir kullanım senaryosu neden olsun, emin değilim. Kullanım alanı aşırı dar görünüyor
Yan yorumlarda ayrıştırmadan söz ediliyor ama idealde önceden bilinen veri yapıları ayrıştırılıyor olmaz mı? Sanki bir şeyi kaçırıyorum
O dilde symbol’leri, yalnızca hızlı eşitlik karşılaştırmasının ötesinde ayrı bir tür olarak tutmanın en az iki avantajı var. Symbol’ler atomlardır; yani karakter listesi türü bir dizi değil, atomik birimlerdir, bu yüzden birçok operatör onları farklı ele alır ve symbol’ler vektörize edilerek tek türden listelerde sıkı biçimde saklanabilir
K ve Q’da veritabanı tablolarının sütunlarını vektörize edilmiş türler olarak temsil etmek çok arzu edilir. Yerellik daha iyidir, bellek daha verimli kullanılır ve birçok operatörde hızlı yollar vardır. Ancak symbol tablosu kısıtları nedeniyle, kardinalitesi yüksek sütunlarda symbol kullanırken dikkatli olmak gerekir
Bilinen şemaya sahip JSON ayrıştırılıyorsa, symbol’ler sözlük anahtarları için harikadır ve k2/k3’te fiilen zorunludur. Ama kimliği belirsiz bir JSON ise, bunun kullanıcı girdisinden gelmemesi gerekir
Bazı K lehçeleri symbol uzunluğunu kısa tutarak 64 bitlik bir değere paketlenip taşınabilmesini sağlar. Genellikten ödün verilir ama bunun karşılığında symbol tablosuna ihtiyaç kalmaz
“Kontrollü girdi” ile “kontrolsüz girdi” ayrımı, güvenlikte
nullolup olmaması gibi geliyorwebpack-plugin-less-cssgüvenilmeyen CSS dosyaları aldığında hizmet engelleme yaşanıyor türü kayıtları görünce CVE yorgunluğu ciddi biçimde hissediliyorYine de burada daha iyi sınır işaretleri olsa güzel olurdu. Örneğin string birleştirmeden geçince hangi güvenlik özelliklerinin korunduğu gibi bileşim kurallarını da iyi ele alabilsek
Ayrıca HTTP POST ile gelen bir sürü şeyi
SafeStringyapıştırıp geçtiyseniz, bu da bir noktaya kadar sizin sorumluluğunuzdur