Selam TurkTeam, BlueTornado ile devam.
Dosya yükleme özelliği; profil fotoğrafı, ek, CV, fatura gibi meşru ihtiyaçlar için vardır. Yanlış uygulanırsa webshell yükleme, XSS (SVG/HTML), path traversal, depolama tüketimi (DoS) ve sunucu tarafı parse açıklarına yol açar. Bu rehber güvenli doğrulama odaklıdır; örnekler DVWA ve kendi labınız içindir.
Konuda Anlatılacaklar
- File upload riskleri nelerdir?
- DVWA File Upload labı
- Tehlikeli doğrulama hataları
- Savunma katmanları: tip, içerik, depolama, sunum
- Uygulamalı güvenli tasarım
- İleri seviye: SVG, zip slip, malware tarama
- Kaynakça ve yasal uyarı
1) Riskler
- Uzaktan kod çalıştırma:,Kod:
.php,Kod:.jspwebshell'in web köküne yazılmasıKod:.aspx - XSS:/Kod:
.htmldosyasının aynı origin'den sunulmasıKod:.svg - Path traversal:dosya adıKod:
../../var/www/shell.php - Overwrite: mevcut kritik dosyanın üzerine yazma
- Zip Slip: arşiv açarken dizin kaçışı
- DoS: çok büyük dosya, zip bomb
OWASP WSTG içinde "Testing for Uploaded File Attacks" bölümü bu aileyi kapsar. CWE örnekleri: CWE-434 (Unrestricted Upload of File with Dangerous Type).
2) Lab: DVWA File Upload
docker compose -f DVWA/docker-compose.yml up -d
# Security Low -> File UploadLow seviyede çoğu kurulum yalnızca istemci tarafı veya basit uzantı kontrolü yapar. Impossible seviyede içerik tipi ve depolama sertleştirmesi vardır.
[center][/center]
Gözlem adımları (lab):
# 1) Küçük bir PNG yükleyin, yükleme yolunu not alın
# 2) Uzantı / Content-Type oyunlarını lab politikasına göre gözlemleyin
# 3) Impossible ile farkı karşılaştırınWebshell yüklemeyi üretimde veya izinsiz sistemlerde denemeyin. Labda bile yüklediğiniz dosyaları işiniz bitince silin.
3) Yaygın Hatalı Kontroller
Sadece istemci tarafı:
<!-- Kolayca bypass: DevTools ile accept kaldırma -->
<input type="file" accept="image/*">Sadece uzantı kara listesi:
# .php engelli ama .php5 .phtml .phar .jpg.php ?
# Sunucu yapılandırmasına bağlıSadece Content-Type başlığı:
Content-Type: image/png
# İstemci istediğini yazar; güvenilmezMIME sniffing/
file4) Güvenli Doğrulama Katmanları
1 - Allowlist: Yalnızca iş ihtiyacı olan uzantılar (
jpg jpeg png webp pdf2 - Rastgele dosya adı: Kullanıcı adını kullanmayın.
import uuid
from pathlib import PurePosixPath
ext = PurePosixPath(filename).suffix.lower()
if ext not in {".jpg", ".jpeg", ".png", ".webp"}:
raise ValueError("uzantı yok")
stored = f"{uuid.uuid4().hex}{ext}"3 - İçerik doğrulama:
from PIL import Image
img = Image.open(upload_stream)
img.verify() # bozuk/şüpheli dosyayı ele
# Yeniden encode ederek metadata/polyglot riskini azaltın
Image.open(upload_stream).save(out_path, format="JPEG")4 - Depolama konumu: Dosyaları web kökü dışına veya yalnızca statik object storage'a koyun. Uygulama sunucusunun yorumladığı dizine yazmayın.
5 - Sunum:
# İndirme için
Content-Disposition: attachment; filename="rapor.pdf"
X-Content-Type-Options: nosniff
Content-Type: application/pdf6 - Boyut ve hız sınırı:
# Nginx örneği
client_max_body_size 5m;[center][/center]
5) Uygulamalı Güvenli İskelet
# Flask kavramsal özet
ALLOWED = {"jpg", "jpeg", "png"}
MAX = 5 * 1024 * 1024
def save_avatar(file_storage):
if file_storage.content_length and file_storage.content_length > MAX:
abort(400)
name = file_storage.filename or ""
ext = name.rsplit(".", 1)[-1].lower()
if ext not in ALLOWED:
abort(400)
# PIL verify + re-encode
# kaydet: /var/app/uploads/{uuid}.jpg (web root dışı)
# sun: imza/lı URL veya ayrı static host# PHP move_uploaded_file + finfo allowlist (kavramsal)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['u']['tmp_name']);
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png'];
if (!isset($allowed[$mime])) { http_response_code(400); exit; }6) İleri Seviye
SVG: İçinde JavaScript / external entity olabilir. Avatar için SVG kabul etmeyin veya sıkı sanitizer + ayrı domain kullanın.
Zip Slip:
# Tehlikeli arşiv girdileri
../../../../tmp/evil
# Açarken final path'in hedef dizin altında kaldığını doğrulayınMalware tarama: ClamAV vb. ile async tarama; temizlenmeden public yapmayın.
ImageMagick / parse CVE'leri: Görüntü işleme kütüphanelerini güncel tutun; policy.xml ile coder'ları kısıtlayın. İcat CVE numarası yazmayın; NVD'den doğrulayın.
# Rapor dili
Bulgu: Unrestricted File Upload (CWE-434)
Kanıt: Labda tehlikeli tipin web köküne yazılması
Öneri: allowlist + re-encode + web root dışı + nosniff[center][/center]
7) Sonuç
Dosya yükleme, "uzantıya bakmak"tan ibaret değildir. Allowlist, içerik doğrulama, güvenli depolama ve güvenli sunum birlikte gerekir. DVWA Low/Impossible farkını inceleyerek kendi uygulamanızın kontrol listesini çıkarın.
Ek: Operasyonel Kontrol Listesi ve Yaygın Senaryolar
Profil fotoğrafı: Yalnızca raster; yeniden encode; sabit max boyut (ör. 1024px); EXIF temizliği (konum sızıntısı).
# ImageMagick (güncel sürüm + policy) ile yeniden örnekleme kavramı
# convert input.jpg -resize 1024x1024> -strip out.jpgPDF yükleme: PDF içinde JavaScript / form aksiyonları olabilir. Inline göstermek yerine
Content-Disposition: attachmentÇok parçalı / chunked upload: Her parçayı ayrı doğrulayın; birleştirme sonrası tekrar MIME/uzantı kontrolü yapın; geçici dosyaları güvenli dizinde tutun.
# S3 / MinIO politikası kavramı
# public-read vermeyin; pre-signed URL ile süreli erişim
# bucket'ta website hosting açıkken html/svg yüklemeyinCI kontrolü: Yükleme ile ilgili birim testleri: path traversal dosya adı reddi, aşırı boyut reddi, yanlış MIME reddi, başarılı PNG yolu.
[center][/center]
Yükleme özelliği ürün için vazgeçilmezse, güvenlik bütçesini "sonra bakarız"a bırakmayın; tasarım aşamasında depolama ve sunum kararlarını kilitleyin.
Kaynakça
Yasal Uyarı
Zararlı dosya yükleme ve webshell denemeleri yalnızca kendi labınızda veya yazılı yetki ile yapılmalıdır. İzinsiz yükleme TCK 243-245 kapsamında suçtur. Eğitim amaçlıdır; sorumluluk uygulayana aittir.
Yasal uyarı: Bu içerik eğitim amaçlıdır. Yalnızca kendi sistemlerinizde veya yazılı izin aldığınız hedeflerde uygulayın (TCK 243-245).