Tüm yazılara dön
·5 dk okuma

Güvenli dosya yükleme: karantina, tarama ve yayımlama hattı

Dosya yükleme özelliği, kullanıcı açısından basit görünür: bir dosya seçilir, yükleme çubuğu dolar ve içerik görünür. Sistem açısından ise bu akış, uygulamanın dışarıdan aldığı en karmaşık girdilerden biridir. Dosyanın adı, uzantısı, MIME türü ve hatta tarayıcının bildirdiği boyut bilgisi güvenilir kabul edilemez. Dosya; zararlı içerik, beklenmeyen boyut, yanlış biçim, hassas veri ya da işlemeyi zorlayan bir belge olabilir.

Bu nedenle güvenli tasarımın temel ilkesi şudur: Bir dosyanın depoya ulaşması, onu uygulamanın geri kalanı için güvenli veya yayımlanabilir yapmaz. Kabul, inceleme ve yayımlama birbirinden ayrı durumlar olmalıdır. Bu ayrım hem saldırı yüzeyini azaltır hem de kullanıcıya dosyasının neden henüz görünür olmadığını doğru biçimde anlatır.

Yükleme akışını bir durum modeli olarak düşünün

Bir yüklenen dosya için açık durumlar tanımlamak, güvenlik ve kullanıcı deneyimini birlikte iyileştirir. Basit bir model şöyle olabilir:

selected → uploading → received → scanning → approved → published
                              └→ rejected
                              └→ failed

received, dosyanın nesne depolamaya ulaştığını söyler; approved ise güvenlik ve iş kurallarından geçtiğini belirtir. published yalnızca yetkili kullanıcıların veya hedef kitlenin erişebileceği durumdur. Dosya taranırken indirilebilir bağlantı üretmemek, “yükleme tamamlandı” ile “içerik hazır” mesajlarını ayırmak gerekir.

Her geçiş sunucuda yetkili bir işlem tarafından yapılmalıdır. Tarayıcının gönderdiği “scan passed” veya “public” benzeri bir alan karar mekanizması olamaz. Kullanıcı arayüzü sadece bu kararların sonucunu gösterir.

Yetkilendirmeyi ilk istekte başlatın

Kullanıcı dosyayı doğrudan uygulama sunucusuna göndermek zorunda değildir. Büyük dosyalarda veya yüksek trafikte, sunucunun kısa ömürlü bir yükleme izni üretmesi ve istemcinin nesne depolamaya doğrudan yüklemesi verimli olabilir. Ancak bu izin, dosya adı üzerinden herkese açık bir URL olmamalıdır.

Yükleme izni oluşturulurken sunucu en az şu kontrolleri yapmalıdır: kullanıcı bu kaynağa dosya eklemeye yetkili mi, hedef alan veya klasör geçerli mi, beklenen dosya boyutu sınır içinde mi ve kabul edilen içerik türleri nelerdir? İzin belirli bir kullanıcıya, tek bir nesne anahtarına, kısa bir süreye ve mümkünse içerik uzunluğu gibi koşullara bağlanmalıdır.

Nesne anahtarını kullanıcıdan gelen dosya adıyla oluşturmak da risklidir. Tahmin edilebilir adlar başka bir kullanıcının dosyasının üzerine yazılmasına veya erişim adreslerinin sızmasına yol açabilir. Sunucunun ürettiği rastgele kimlikler, orijinal dosya adından ayrı saklanmalı; görüntüleme sırasında güvenli bir indirme adı kullanılmalıdır.

Dosyayı karantinada saklayın

Yükleme tamamlandığında dosya doğrudan genel erişimli bir bucket'a ya da uygulamanın kullandığı kalıcı alana taşınmamalıdır. Önce ayrı bir karantina konumuna yazılmalıdır. Bu konumda dosya için public URL, CDN dağıtımı veya başka kullanıcıların erişebileceği önizleme etkin olmamalıdır.

Karantina kaydında yükleyen kullanıcı, hedef kaynak, nesne anahtarı, bildirilen içerik türü, boyut, checksum ve zaman damgası gibi teknik metadata tutulabilir. Bu kayıt, hem tarama işinin girdisi hem de olay incelemesinin başlangıç noktasıdır. Ancak metadata da kullanıcı girdisi içerdiğinden, yönetim ekranlarında HTML veya kabuk komutu olarak yorumlanmamalıdır.

Karantinanın ayrı tutulması, başarısız tarama durumunda temizlik yapmayı da kolaylaştırır. Reddedilen nesne bir süre denetim amacıyla saklanabilir, ardından yaşam döngüsü kuralıyla silinebilir. Böylece uygulamanın ana depolaması yalnızca onaylanmış nesneleri içerir.

İçerik türünü birden fazla katmanda doğrulayın

.pdf uzantılı her dosyanın PDF olduğunu varsaymak yeterli değildir. Kontroller katmanlı olmalıdır:

  • İstemci tarafı, kullanıcıya hızlı geri bildirim için uzantı ve boyut kontrolü yapabilir.
  • Sunucu, izin üretirken beklenen tür ve boyut için politika uygular.
  • Tarama çalışanı, dosyanın imzasını veya magic byte'larını kontrol eder.
  • Gerekirse biçime özgü ayrıştırıcı, belgenin gerçekten işlenebilir olup olmadığını doğrular.

Bu katmanların hiçbiri tek başına güvenlik sınırı değildir. İstemci kontrolü kullanıcı deneyimi içindir ve kolayca atlatılabilir; MIME başlığı taklit edilebilir; biçim ayrıştırıcıları ise kendi saldırı yüzeyine sahip olabilir. Bu yüzden ayrıştırma ve dönüştürme işlemleri sınırlı kaynaklara sahip, ana uygulamadan ayrılmış işçi süreçlerinde çalışmalıdır.

Tarama işini asenkron ve tekrar çalıştırılabilir kılın

Virüs taraması, içerik moderasyonu, OCR veya belge dönüştürme isteğin yanıtını uzatabilir. Bu işleri yükleme isteği içinde çalıştırmak, zaman aşımı ve kötü kullanıcı deneyimi oluşturur. Daha dayanıklı akışta karantina kaydı bir kuyruğa iş bırakır; worker dosyayı tarar ve sonucu atomik biçimde dosya durumuna yazar.

Mesajlar tekrar teslim edilebileceği için işçi idempotent olmalıdır. Aynı dosya iki kez taranırsa ikinci işlem yeni bir yayımlama yapmamalı, mevcut sonucu güvenle tanımalıdır. Çalışan çökerse veya harici tarama aracı geçici hata verirse durum failed olabilir ve kontrollü tekrar deneme planlanabilir. Tarama sonucunu “belirsiz” bırakmak yerine, süre aşımında dosyayı karantinada tutmak güvenli varsayımdır.

Tarama tamamlanınca yalnızca onaylanan nesne, kalıcı özel depoya taşınmalı veya erişim politikası değiştirilmelidir. Taşıma işlemi ve durum güncellemesi birbirinden koparsa ortaya çıkan tutarsızlığı giderecek işlem kayıtları ve onarım işi bulunmalıdır.

İndirme ve önizlemeyi de koruyun

Dosya onaylanmış olsa bile erişim kontrolü biter sanılmamalıdır. İndirme isteğinde kullanıcının ilgili kaynağı görme yetkisi tekrar doğrulanmalıdır. Kısa ömürlü imzalı URL'ler, özel nesneleri uygulama sunucusundan aktarmadan yetkili kullanıcıya vermek için yararlı olabilir. Bu URL'ler yine tahmin edilmemeli, gereksiz uzun ömürlü olmamalı ve mümkünse tek nesneyle sınırlandırılmalıdır.

Tarayıcıda önizlenecek kullanıcı içeriği özellikle dikkat ister. HTML veya SVG gibi etkin içerik türlerini uygulamanın kendi alan adında sunmak XSS riskini artırabilir. Güvenilmeyen içeriği ayrı bir origin üzerinden, indirme zorlamasıyla veya dönüştürülmüş güvenli bir temsil olarak sunmak daha doğru olabilir. Content-Disposition, içerik güvenlik politikası ve nosniff gibi başlıklar savunmayı tamamlar.

Kullanıcıya doğru durumu, ekibe doğru sinyali verin

“Yükleme başarısız” ile “dosya yüklendi ancak güvenlik incelemesinde reddedildi” aynı mesaj değildir. Kullanıcıya neyin gerçekleştiğini, tekrar deneme gerekip gerekmediğini ve destek gerektiren durumları anlaşılır biçimde söylemek gerekir. Reddedilme nedenini paylaşırken de saldırganın kontrolleri aşmasına yardım edecek gereksiz ayrıntılardan kaçınılmalıdır.

Ekip tarafında yükleme başarısı, tarama gecikmesi, ret oranı, tekrar deneme, kuyruk yaşı ve depolama hatası gibi metrikler izlenmelidir. Loglar nesne kimliği ve işlem sonucu içerebilir; fakat dosya içeriği, gizli URL veya hassas belge metni içermemelidir.

Güvenli dosya yükleme, tek bir antivirüs çağrısı değildir. Yetkili kabul, karantina, katmanlı doğrulama, asenkron tarama, kontrollü yayımlama ve erişim denetimi birlikte çalıştığında, kullanıcıların içerik paylaşmasını kolaylaştırırken uygulamanın güvenlik sınırını da korur.