PHP

PHP’de Dosya Yükleme İşlemi

PHP'de Dosya Yükleme İşlemi

Web uygulamalarında kullanıcıların profil fotoğrafı yüklemesi, belge paylaşması veya rapor göndermesi oldukça yaygın gereksinimlerdir. PHP, bu işlemleri gerçekleştirmek için yerleşik mekanizmalara sahiptir. Ancak dosya yükleme mekanizması, doğru kurgulanmadığında sunucu güvenliğini doğrudan tehlikeye atabilecek en kritik alanlardan biridir. Bu rehberde, PHP ile dosya yükleme mantığını temel seviyeden ele alacak ve güvenli bir yükleme yapısının nasıl kurulacağını inceleyeceğiz.

Dosya Yükleme Sürecinin İşleyişi

Kullanıcı tarayıcısından bir dosya seçip formu gönderdiğinde, dosya doğrudan hedef klasöre kaydedilmez. İlk olarak sunucunun geçici dizinine (temporary directory) geçici bir isimle aktarılır. PHP betiği çalıştığı anda bu dosyanın meta verilerine erişebilir, gerekli doğrulamaları yapabilir ve uygun gördüğü takdirde dosyayı kalıcı dizine taşıyabilir. Betik sonlandığında taşınmayan tüm geçici dosyalar sistem tarafından otomatik olarak silinir.

Bu sürecin sağlıklı işlemesi için sunucudaki php.ini yapılandırma dosyasında belirli yönergelerin doğru ayarlanmış olması gerekir:

  • file_uploads: Dosya yükleme özelliğinin açık olup olmadığını belirler (varsayılan: On).
  • upload_max_filesize: Tek bir dosya için izin verilen maksimum boyutu tanımlar.
  • post_max_size: Tek bir POST isteğinde gönderilebilecek toplam veri boyutunu belirler. Bu değer, upload_max_filesize değerinden büyük olmalıdır.
  • upload_tmp_dir: Dosyaların geçici olarak tutulacağı dizini belirtir.

İstemci Tarafı: HTML Formunun Hazırlanması

Dosya yükleme işleminin başlayabilmesi için istemci tarafında uygun bir HTML formu bulunmalıdır. Standart formlardan farklı olarak dosya gönderiminde enctype niteliği zorunludur.

<form action="yukle.php" method="POST" enctype="multipart/form-data">
    <label for="dosya">Yüklenecek Dosyayı Seçin:</label>
    <input type="file" name="yuklenen_dosya" id="dosya">
    <button type="submit">Gönder</button>
</form>

Yukarıdaki formda iki kritik nokta bulunur. İlki, method değerinin mutlaka POST olmasıdır; GET yöntemi ile dosya ikili verileri (binary data) aktarılamaz. İkincisi ise enctype="multipart/form-data" özniteliğidir. Bu öznitelik, form verilerinin metin dışında ikili veriler de içereceğini tarayıcıya bildirir. Bu tanım eksik olduğunda dosya sunucuya iletilmez.

Sunucu Tarafı: $_FILES Süper Küreseli

Form gönderildiğinde, PHP gelen dosya bilgilerini $_POST yerine $_FILES süper küresel dizisi içine yerleştirir. HTML formundaki name özniteliği bu dizinin anahtarı olur. Örneğin, $_FILES['yuklenen_dosya'] yapısı şu alt anahtarları barındırır:

  • name: Dosyanın kullanıcının bilgisayarındaki orijinal adı.
  • type: Tarayıcı tarafından bildirilen MIME türü (kullanıcı manipülasyonuna açıktır).
  • tmp_name: Dosyanın sunucuda barındırıldığı geçici yol ve dosya adı.
  • error: Yükleme sırasında oluşan hata kodu (0 veya UPLOAD_ERR_OK hatasız olduğunu gösterir).
  • size: Dosyanın bayt (byte) cinsinden boyutu.

Güvenli Dosya Yükleme Mimarisi

Kullanıcıdan gelen verilere hiçbir zaman doğrudan güvenilmemelidir. Salt dosya uzantısını veya tarayıcının bildirdiği MIME türünü kontrol etmek yeterli değildir. Kötü niyetli bir kullanıcı, bir PHP betiğini resim.jpg gibi gösterip yükleyebilir veya uzantı filtrelerini aşarak doğrudan web kök dizininde zararlı kod çalıştırabilir (Remote Code Execution – RCE).

Güvenli bir dosya yükleme süreci şu adımları içermelidir:

1. Hata Kontrolü

Yükleme işleminin işletim sistemi veya sunucu limitleri nedeniyle kesintiye uğrayıp uğramadığı $_FILES['yuklenen_dosya']['error'] üzerinden denetlenmelidir.

2. Boyut Sınırlandırması

Sunucu kaynaklarının tükenmesini (Denial of Service) önlemek için uygulama bazında sıkı bir dosya boyutu sınırı konulmalıdır.

3. MIME Türü Doğrulaması

Kullanıcının beyan ettiği MIME türü yerine, dosyanın ikili içeriği incelenmelidir. PHP’nin yerleşik finfo (File Information) modülü, dosyanın bayt yapısını okuyarak gerçek MIME türünü tespit eder.

4. Dosya Adının Yeniden Oluşturulması

Kullanıcının gönderdiği orijinal dosya adı doğrudan kullanılmamalıdır. Dizin geçişi (Path Traversal) saldırılarını engellemek ve çakışmaları önlemek için dosyalara rastgele, benzersiz isimler atanmalıdır.

5. Yükleme Dizinine Çalıştırma Yasağı

Dosyaların yüklendiği klasörde PHP betiklerinin çalıştırılması engellenmelidir. En güvenli yöntem, dosyaları web erişimine kapalı (web root dışında) bir dizinde tutmaktır.

Uygulama: Güvenli Dosya Yükleme Kodu

Aşağıdaki örnek, yalnızca PNG ve JPEG formatındaki resimlerin kabul edildiği, 2 MB boyut sınırına sahip ve dosya adını rastgele üreten güvenli bir PHP betiğidir.

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_FILES['yuklenen_dosya']) || $_FILES['yuklenen_dosya']['error'] !== UPLOAD_ERR_OK) {
        die('Dosya yüklenirken bir hata oluştu veya dosya seçilmedi.');
    }

    $dosya = $_FILES['yuklenen_dosya'];

    // 1. Boyut kontrolü (2 MB = 2 * 1024 * 1024 bayt)
    $maksimumBoyut = 2097152;
    if ($dosya['size'] > $maksimumBoyut) {
        die('Hata: Dosya boyutu 2 MB sınırını aşıyor.');
    }

    // 2. Gerçek MIME türü kontrolü
    $izinVerilenTurler = [
        'image/jpeg' => 'jpg',
        'image/png'  => 'png'
    ];

    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $gercekMimeTuru = $finfo->file($dosya['tmp_name']);

    if (!array_key_exists($gercekMimeTuru, $izinVerilenTurler)) {
        die('Hata: Yalnızca JPEG ve PNG dosyaları kabul edilmektedir.');
    }

    // 3. Güvenli dosya adı üretimi
    $uzanti = $izinVerilenTurler[$gercekMimeTuru];
    $yeniDosyaAdi = bin2hex(random_bytes(16)) . '.' . $uzanti;

    // 4. Hedef dizin kontrolü ve dosya taşıma
    $hedefDizin = __DIR__ . '/yuklemeler/';
    if (!is_dir($hedefDizin)) {
        mkdir($hedefDizin, 0755, true);
    }

    $hedefYol = $hedefDizin . $yeniDosyaAdi;

    if (move_uploaded_file($dosya['tmp_name'], $hedefYol)) {
        echo 'Dosya başarıyla yüklendi: ' . htmlspecialchars($yeniDosyaAdi, ENT_QUOTES, 'UTF-8');
    } else {
        echo 'Dosya kaydedilirken sunucu hatası oluştu.';
    }
}
?>

Yukarıdaki kod bloğunda:

  • finfo sınıfı ile dosyanın gerçek imzası okunmuş, sahte uzantı saldırıları engellenmiştir.
  • bin2hex(random_bytes(16)) fonksiyonu ile tahmin edilemez 32 karakterlik bir dosya adı oluşturulmuştur.
  • move_uploaded_file() fonksiyonu kullanılmıştır. Bu fonksiyon, taşınacak dosyanın gerçekten geçerli bir HTTP POST yüklemesi olup olmadığını dahili olarak teyit eder. Dosyayı doğrudan rename() veya copy() ile taşımak güvenlik zaafiyetlerine yol açabilir.

Sonuç ve Sıradaki Adımlar

PHP ile dosya yükleme işlemlerinde form yapısının doğru ayarlanması işin yalnızca ilk adımıdır. Asıl sorumluluk, sunucu tarafında dosyanın içeriğini denetlemek, boyut sınırlarını işletmek ve rastgele isimlendirme yaparak olası enjeksiyon saldırılarını bertaraf etmektir. Bir sonraki aşamada, yüklenen görsellerin GD veya Imagick kütüphaneleriyle yeniden boyutlandırılarak temizlenmesini sağlayabilir veya dosyaları doğrudan bir bulut depolama servisine aktaracak mimarileri inceleyebilirsiniz.

#backend geliştirme#dosya yukleme#php#web guvenligi

Comments

No comments yet. Be the first to comment.

Leave a comment

Comments are published after the admin approves them.