Tedarik Zinciri · Model Provenance

Pickle mı Safetensors mı? Model Dosya Formatlarında Kod Çalıştırma Riski
7 formatın deserialization risk tablosu

Bir model dosyasını yüklemek çoğu zaman "veri okumak" değil, "kod çalıştırmak" demektir. Pickle tabanlı formatlar dosyayı açtığınız anda keyfi komut çalıştırabilir; safetensors ise tasarımı gereği bunu yapamaz. Aradaki farkı opcode ve operatör düzeyinde görmek, tedarik zinciri güvenliğinin en somut kararıdır.

Model dosya formatı güvenliği nedir?

Model dosya formatı güvenliği, bir yapay zeka modelinin diske serileştirilme biçiminin — ağırlıkların, meta verinin ve bazı formatlarda gömülü mantığın hangi kodlamayla saklandığının — o dosya yüklendiğinde saldırgan kontrollü kod çalıştırılmasına izin verip vermediğini inceleyen alt disiplindir. Kritik ayrım şudur: bazı formatlar dosyayı yüklemeyi saf veri okuma işlemi olarak tutar; bazıları ise yükleme sırasında rastgele fonksiyon çağırmaya izin veren bir serileştirme protokolü kullanır. İkinci gruptaki bir dosyayı torch.load() veya joblib.load() ile açmak, pratikte tanımadığınız bir betiği çalıştırmakla eşdeğerdir.

Bu yüzden model indirmek, klasik bir tedarik zinciri kararıdır: modeli nereden aldığınız kadar, hangi formatta aldığınız da saldırı yüzeyini belirler. Güvenilmeyen bir kaynaktan gelen pickle tabanlı bir ağırlık dosyası, imzalı bir paket kadar ciddiye alınması gereken bir yürütülebilir eserdir.

Pickle neden kod çalıştırır?

Python'un pickle modülü bir serileştirici değil, küçük bir sanal makinedir. Dosya, bir yığın (stack) makinesi için opcode akışıdır; pickle.load() bu opcode'ları sırayla yorumlar. Sorun, protokolün nesne yeniden inşasını fonksiyon çağrısıyla modellemesidir.

  • GLOBAL / STACK_GLOBAL: bir modül ve isim çiftinden herhangi bir Python nesnesini (örneğin os.system, builtins.eval, subprocess.Popen) yığına yükler.
  • REDUCE: yığındaki çağrılabiliri, yine yığındaki argümanlarla çağırır — yani gerçek yürütme burada gerçekleşir.
  • Bir sınıfın __reduce__ / __reduce_ex__ metodu, o nesnenin nasıl "yeniden kurulacağını" tanımlar; saldırgan buraya (os.system, ("komut",)) döndürerek yükleme anında komut çalıştırır.

Yani zararlı bir pickle, geçerli ve çoğu zaman fonksiyonel bir model dosyasıdır; sadece opcode akışına yükleme sırasında tetiklenen birkaç çağrı gizlenmiştir. Python resmi belgeleri bu davranışı doğrudan uyararak belirtir: güvenilmeyen veriyi asla unpickle etmeyin.

Pickle ailesi: joblib ve PyTorch .bin de aynı riski taşır

Yaygın bir yanılgı, riskin yalnızca .pkl uzantılı dosyalarda olduğudur. Gerçekte pickle bir ailedir ve popüler formatların çoğu onun üstüne kuruludur:

  • joblib: scikit-learn ekosisteminde standarttır; ayrı bir güvenlik modeli yoktur, altında pickle protokolünü kullanır. "Ayrı format" gibi görünse de aynı REDUCE/GLOBAL tetikleyicilerine açıktır.
  • PyTorch .bin / .pt: torch.save varsayılan olarak pickle sarmalayıcısı üretir; torch.load bunu unpickle eder. PyTorch, güvenlik gerekçesiyle weights_only=True seçeneğini 2.6 sürümünden itibaren varsayılan hâline getirdi — bu mod, izin listesi dışındaki genel çağrılabilirleri reddederek saldırı yüzeyini daraltır. Ancak eski kod ve eski ağırlıklar hâlâ sınırsız torch.load ile yükleniyor.

Kısaca: uzantıya değil, altta yatan serileştirme protokolüne bakın. .bin, .pt, .pkl, .joblib ve .ckpt pratikte aynı tehdit sınıfındadır.

Safetensors gerçekten güvenli mi?

Evet — ve nedeni mimari bir tercihtir, iyi niyet değil. Safetensors formatı iki parçadan oluşur: başta bir JSON başlığı (tensör adları, veri tipleri, şekiller ve bayt aralıkları) ve ardından ham tensör baytları. Format içinde çağrılabilir nesne, opcode, fonksiyon referansı ya da "nesneyi yeniden kur" mekanizması yoktur. Yükleyici yalnızca başlığı ayrıştırır, aralıkları okur ve baytları tensöre eşler. Kod çalıştıracak bir yol tanım gereği bulunmadığı için, güvenilmeyen bir safetensors dosyasını yüklemek uzaktan kod çalıştırma vektörü açmaz.

Bu, safetensors'un her riski sıfırladığı anlamına gelmez. Bozuk bir başlık ayrıştırıcıyı zorlayabilir, kötü şekillendirilmiş uzunluk alanları hizmet reddi (DoS) veya aşırı bellek ayırma denemesi doğurabilir. Ama bunlar bellek güvenliği ve girdi doğrulama sınıfındaki sorunlardır — pickle'daki "yükleme = yürütme" sınıfından niteliksel olarak farklıdır. Pratik sonuç nettir: bir modelin safetensors sürümü mevcutsa, pickle sürümünü tercih etmek için hiçbir güvenlik gerekçesi yoktur.

GGUF, ONNX ve TensorFlow SavedModel: abartısız risk seviyeleri

Bu üç formatı "hepsi tehlikeli" ya da "hepsi güvenli" diye tek kefeye koymak yanlış olur; her birinin risk profili farklıdır.

  • GGUF (llama.cpp ekosistemi): büyük ölçüde veri-only bir formattır. Meta veri anahtar-değer çiftleri ve nicemlenmiş tensörler taşır; içinde serileştirilmiş çağrılabilir mantık yoktur. Buradaki gerçekçi risk, kod çalıştıran opcode değil, ayrıştırıcıdaki bellek güvenliği hatalarıdır (hatalı uzunluk/ofset alanlarının tetiklediği taşmalar). Yani risk kaynağı formatın kendisi değil, onu okuyan C/C++ ayrıştırıcısının sağlamlığıdır.
  • ONNX: standart operatör grafiği veri-only'dur. Risk koşulludur: model özel bir operatöre (custom op) referans veriyorsa ve çalışma ortamı bu operatörü sağlayan yerel bir uzantıyı yüklüyorsa, yürütme o yerel kodda gerçekleşir. Yani tehlike ONNX baytlarında değil, güvenilmeyen özel operatör kütüphanelerini kaydetme pratiğindedir.
  • TensorFlow SavedModel: en incelikli olanıdır. Serileştirilmiş grafik, dosya sistemine erişen operatörler (örneğin ReadFile/WriteFile) veya keras Lambda katmanı gibi gömülü Python mantığı içerebilir. Bu yollar, güvenilmeyen bir SavedModel'i yüklemeyi/çalıştırmayı gerçek bir kod ve dosya erişimi riskine dönüştürür. Bu yüzden SavedModel'i pickle'a yakın, ancak koşullu bir risk sınıfında değerlendiriyoruz.

Yedi formatlı model dosyası risk tablosu

Aşağıdaki tablo, bir modeli yüklerken sorulması gereken üç soruyu tek bakışta toparlıyor: kod çalıştırma yolu var mı, hangi mekanizma üzerinden, statik bir tarayıcı bunu yakalayabilir mi. Türkçe kaynaklarda az işlenen bu karşılaştırmayı, yükleme öncesi karar vermek için başvuru olarak kullanabilirsiniz.

FormatKod çalıştırma yolu?Mekanizma (opcode/operatör)Tarayıcı yakalar mı?Risk
pickle (.pkl)EvetREDUCE / GLOBAL / STACK_GLOBAL, __reduce__Kısmen (statik opcode taraması)Yüksek
joblib (.joblib)Evet (pickle ailesi)Aynı pickle opcode'larıKısmenYüksek
PyTorch .bin / .ptEvet (pickle sarmalı)pickle opcode + persistent_loadKısmen; weights_only daraltırYüksek
safetensorsHayır (tasarım gereği)Yok — JSON başlığı + ham tensörGerek yok (başlık DoS'u ayrı)Düşük
GGUFBüyük ölçüde hayır (veri-only)Yok; ayrıştırıcı bellek hatalarıSınırlı (yapı doğrulama)Düşük-Orta
ONNXKoşulluÖzel operatör / yerel runtime uzantısıKısmen (custom-op referansı)Orta
TF SavedModelEvet (koşullu)Lambda katmanı / dosya op'ları (ReadFile)SınırlıOrta-Yüksek

"Tarayıcı yakalar mı?" kolonundaki "kısmen" ifadesi kasıtlıdır: statik pickle tarayıcıları bilinen tehlikeli global referanslarını (os, subprocess, builtins) yakalar, ancak dolaylı gadget zincirleri ve obfüskasyon tekniklerine karşı garanti vermezler. Tarama, güvenli varsayılanın (safetensors) yerine geçmez; onu tamamlar.

Zararlı model dosyasını tespit etmek: gerçek tetik listesi

AltaySec'in hf-dataset-scan CI tarayıcısını kurarken çıkardığımız pratik ders şuydu: pickle taramasında "yakalanacak şey" soyut değil, oldukça somut bir işaret kümesidir. Aşağıdaki liste, bir pickle akışını yüklemeden statik olarak incelerken alarm ürettiğimiz gerçek deserialization tetikleyicileridir (AltaySec'in kendi tarayıcı kuralları):

  • GLOBAL / STACK_GLOBAL ile os, posix, nt, subprocess, sys, socket, builtins modüllerine referans,
  • builtins.eval, builtins.exec, builtins.compile, __import__ gibi çağrılabilirlerin yüklenmesi,
  • REDUCE opcode'unun tehlikeli bir global ile aynı yığın bağlamında görünmesi,
  • PyTorch dosyalarında persistent_load üzerinden depolama yeniden eşleme gadget'ları,
  • bilinen zararlı sınıf/fonksiyon imzaları ve şüpheli modül-isim çiftleri.

Bu tarayıcı kod eserini değil, gömülü veriyi hedefler; ama aynı ilke model ağırlıklarına birebir taşınır. CI hattınıza koyacağınız kural basittir: güvenilmeyen kaynaktan gelen pickle tabanlı bir dosya, yükleme öncesi statik taramadan geçmeden üretime giremez. Daha geniş tedarik zinciri bağlamı için HuggingFace model hub tedarik zinciri riskleri ve model imzalama ve provenance yazılarımız bu taramayı imza doğrulamayla birleştirir.

Pratik savunma: format hijyeni ve çalışma zamanı sınırları

Doğru refleks, tek tek CVE kovalamak değil, format hijyenini varsayılan hâle getirmektir:

  • Safetensors'u varsayılan seçin. Bir modelin safetensors sürümü varsa, pickle sürümünü indirmeyin. Mümkünse depo politikanızı safetensors'a kilitleyin.
  • PyTorch'ta weights_only=True kullanın. Eski ağırlıkları yüklerken bile bu mod, izin listesi dışı çağrılabilirleri reddederek en yaygın vektörü kapatır.
  • Yükleme öncesi tarayın. Pickle/joblib/.bin dosyalarını üretime almadan önce statik opcode taramasından geçirin ve sonucu CI kapısı yapın.
  • Yüklemeyi izole edin. Kaçınılmaz olarak güvenilmeyen bir pickle'ı açmanız gerekiyorsa, ağ ve dosya sistemi erişimi kısıtlı bir sandbox içinde, en az yetki ilkesiyle yapın.
  • Provenance zincirini doğrulayın. İmza ve kaynak doğrulaması, formatın taşıdığı riski tümüyle ortadan kaldırmasa da güvenilmeyen eserin üretime sızma olasılığını düşürür.

Çalışma zamanında, model çıktısı ve araç çağrılarını denetleyen bir LLM güvenlik duvarı katmanı (AltaySec Guardian gibi) bu tedarik zinciri kontrollerinin tamamlayıcısıdır; ama tekrar edelim: çalışma zamanı savunması, yükleme anında kod çalıştıran bir dosyayı hiç almamış olmanın yerine geçmez. Model formatı güvenliğinde en ucuz kazanç, riski daha üretilmeden formatta budamaktır. Kurumsal bir model tedarik politikası kurmak için bizimle iletişime geçebilirsiniz.

Kaynaklar

İlgili Yazılar