Dosya yükleme, bir web uygulamasında en sık açık çıkan özelliklerden biri. Profil fotoğrafı, belge, fatura, ek dosya: her biri kullanıcıdan gelen ve sunucunuzda saklanacak güvenilmez bir girdi. İncelediğimiz projelerde sorun genellikle karmaşık bir saldırıdan değil, üç basit eksiklikten çıkıyor: yanlış doğrulanan tür, denetlenmeyen boyut ve yanlış yere kaydedilen dosya.
Tür doğrulaması: uzantıya ve istemciye güvenmeyin
$_FILES['dosya']['type'] değeri tarayıcının gönderdiği bilgidir ve saldırgan tarafından serbestçe değiştirilebilir. Uzantı kontrolü de tek başına yetmez; shell.php.jpg gibi adlar ya da sunucu yapılandırmasına göre çalıştırılan çift uzantılar sorun yaratır. Güvenilir olan, dosyanın içeriğine bakmaktır:
finfo_open(FILEINFO_MIME_TYPE)ile sunucu tarafında gerçek MIME türünü okuyun.- İzin verilen türleri bir beyaz listede tutun; "şunlar hariç hepsi" mantığı kurmayın.
- Görsel yüklüyorsanız
getimagesize()ya da GD ile görseli yeniden işleyip kaydedin; bu hem bozuk içeriği hem gömülü kodu temizler. - Laravel'de
mimetypesvemimesdoğrulama kuralları içeriğe bakar; yine de beyaz listeyi siz belirleyin.
Boyut sınırı: üç yerde birden
Boyut sınırı yalnızca uygulama koduna konulursa yetersiz kalır. PHP'de upload_max_filesize ve post_max_size ayarları, web sunucusunda ise Nginx'in client_max_body_size değeri devreye girer. Bu değerler uyumsuzsa kullanıcı anlamsız bir hata görür ya da büyük istek sunucuya kadar ulaşıp kaynak tüketir. Önce sunucu seviyesinde makul bir üst sınır koyun, ardından uygulamada her form için kendi sınırını doğrulayın ve kullanıcıya açık bir mesaj verin.
Yükleme sırasında $_FILES['dosya']['error'] değerini de kontrol edin. UPLOAD_ERR_OK dışındaki her kod bir sorundur; bunları yok sayıp devam eden kod, yarım kalmış dosyaları kaydedebilir.
Depolama hataları
En tehlikeli hata, yüklenen dosyayı web kökü altında, orijinal adıyla saklamaktır. Önerdiğimiz düzen şöyle:
- Dosya adını kullanıcıdan almayın; rastgele ve benzersiz bir ad üretin, orijinal adı gerekiyorsa veritabanında tutun.
- Uzantıyı kullanıcının verdiği addan değil, doğrulanmış MIME türünden türetin.
- Herkese açık olması gerekmeyen dosyaları web kökünün dışında, Laravel'de
storage/appaltında tutun ve kontrollü bir uç noktadan sunun. - Yüklenen dizinde betik çalıştırmayı sunucu yapılandırmasıyla kapatın.
move_uploaded_file()kullanın;rename()veyacopy()ile taşımak yükleme doğrulamasını atlar.
İndirme tarafını da düşünün
Yükleme güvenli olsa bile sunum sırasında hata yapılabilir. Dosyayı sunarken Content-Type başlığını doğrulanmış türden verin, kullanıcı içeriği için Content-Disposition: attachment tercih edin ve X-Content-Type-Options: nosniff başlığını ekleyin. Dosya yolunu istekten gelen bir parametreyle birleştiriyorsanız ../ dizin gezme denemelerine karşı yolu realpath() ile çözüp beklenen dizinin içinde kaldığını doğrulayın. Yetki kontrolünü de unutmayın: bir kullanıcı yalnızca kendi dosyasına erişebilmeli, tahmin edilebilir kimliklerle başkasınınkine ulaşamamalı.
Büyük dosyalar ve virüs taraması
Büyük dosya yüklemelerinde parçalı yükleme ya da doğrudan nesne depolamaya imzalı adresle yükleme, PHP süreçlerini serbest bırakır. Kullanıcıdan gelen belgelerin başkalarına dağıtılacağı sistemlerde ise ClamAV gibi bir tarayıcıyı kuyruk işi olarak çalıştırmak makul bir ek katmandır. Taramadan geçmeyen dosyayı "beklemede" olarak işaretleyin ve geçene kadar sunmayın.
Yeni bir yükleme alanı yazarken kendinize şu soruları sorun: Dosyanın türünü içeriğinden mi doğruluyorum? Boyut sınırı sunucuda ve uygulamada tutarlı mı? Dosya adını ben mi üretiyorum? Dosya web kökünün dışında mı ve yalnızca yetkili kullanıcıya mı sunuluyor? Dört sorunun dördüne de net bir evet diyemiyorsanız, o yükleme alanı henüz hazır değildir. Bu liste kod incelemesinde de işe yarıyor, çünkü tek tek açıkları aramak yerine eksik cevabı bulmak yeterli oluyor.
Uygulamanızdaki yükleme akışlarının güvenliğini gözden geçirmek ya da yeni bir belge yönetimi modülünü baştan sağlam kurmak isterseniz iletişim sayfamızdan bize yazın. Bu tür açıkları çoğu zaman yeni bir özellik yazmadan, birkaç satırlık doğru kontrolle kapatabiliyoruz.