İçeriğe geç

Dosya İşlemleri: PHP ile Dosya Okuma, Yazma ve Upload

PHP, web uygulamaları geliştirmek için vazgeçilmez bir dil olup, dosya sistemleriyle etkileşim kurma yeteneği sayesinde dinamik içerik yönetimine olanak tanır. Sunucu üzerindeki dosyaları okumak, yazmak, düzenlemek veya kullanıcıların dosya yüklemesine izin vermek gibi işlemler, birçok modern uygulamanın temelini oluşturur. Bu yetenekler, basit metin dosyalarından karmaşık medya dosyalarına kadar geniş bir yelpazedeki veri türleriyle çalışmayı kapsar. Ancak, dosya sistemi operasyonları güçlü olduğu kadar, dikkatsiz kullanıldığında ciddi güvenlik açıkları yaratabilir. Bu kapsamlı yazı, PHP’nin dosya sistemi işlevlerini detaylı bir şekilde ele alacak, fopen(), fread(), fwrite(), file_get_contents() ve file_put_contents() gibi temel fonksiyonları inceleyecek, dosya yükleme süreçlerini $_FILES süper globali ile açıklayacak ve bu operasyonları gerçekleştirirken uygulanması gereken kritik güvenlik önlemlerini vurgulayacaktır. Amacımız, hem işlevsellik hem de güvenlik açısından sağlam dosya yönetimi pratikleri oluşturmaktır.

Temel dosya i̇şlemleri: okuma ve yazma

PHP’de dosya sistemleriyle doğrudan etkileşime geçmenin en temel yolu, fopen() fonksiyonunu kullanarak bir dosya akışı açmaktır. Bu fonksiyon, belirtilen dosyayı belirli bir modda açar ve dosya üzerinde okuma veya yazma işlemleri yapmamızı sağlayan bir kaynak (resource) döndürür. Başarısız olursa false döndürür, bu yüzden her zaman dönüş değerini kontrol etmek önemlidir.

fopen(string $filename, string $mode) fonksiyonundaki modlar:

  • 'r': Sadece okuma için açar. Dosya işaretçisi dosyanın başına yerleştirilir. Dosya yoksa hata verir.
  • 'w': Sadece yazma için açar. Dosya işaretçisi dosyanın başına yerleştirilir ve dosyanın içeriğini siler. Dosya yoksa oluşturulur.
  • 'a': Sadece yazma için açar. Dosya işaretçisi dosyanın sonuna yerleştirilir. Dosya yoksa oluşturulur.
  • 'x': Sadece yazma için açar. Dosya zaten varsa false döndürür ve hata verir. Yeni dosya oluşturmak için kullanılır.
  • 'r+': Okuma ve yazma için açar. Dosya işaretçisi dosyanın başına yerleştirilir.
  • 'w+': Okuma ve yazma için açar. Dosyanın içeriğini siler veya dosya yoksa oluşturur.
  • 'a+': Okuma ve yazma için açar. Dosya işaretçisi dosyanın sonuna yerleştirilir. Dosya yoksa oluşturur.

Bir dosya akışını açtıktan sonra, fread() fonksiyonu ile dosya içeriğini okuyabiliriz. Bu fonksiyon, belirtilen bayt sayısını okur ve döndürür.


<?php
$dosya = 'veriler.txt';
$icerik = '';

$handle = fopen($dosya, 'r');
if ($handle) {
    while (!feof($handle)) {
        $icerik .= fread($handle, 1024); // Her seferinde 1024 bayt oku
    }
    fclose($handle);
    echo "<p>Okunan içerik:<br><pre>" . htmlspecialchars($icerik) . "</pre></p>";
} else {
    echo "<p>Dosya okunamadı: $dosya</p>";
}
?>
    

Dosyaya veri yazmak için fwrite() fonksiyonunu kullanırız. Bu fonksiyon, bir dizeyi dosya akışına yazar ve yazılan bayt sayısını döndürür.


<?php
$dosya = 'log.txt';
$yeniVeri = "Bu bir log mesajıdır: " . date('Y-m-d H:i:s') . "\n";

$handle = fopen($dosya, 'a'); // Varolan dosyanın sonuna ekle
if ($handle) {
    if (fwrite($handle, $yeniVeri) !== false) {
        echo "<p>Veri başarıyla dosyaya yazıldı.</p>";
    } else {
        echo "<p>Veri yazılırken bir hata oluştu.</p>";
    }
    fclose($handle);
} else {
    echo "<p>Dosya yazılamadı: $dosya</p>";
}
?>
    

Tüm dosya işlemleri tamamlandığında, fclose() fonksiyonu ile dosya akışını kapatmak hayati önem taşır. Bu, kaynakların serbest bırakılmasını sağlar ve açık dosya kilitlerinin sistem kaynaklarını tüketmesini engeller.

Basitleştirilmiş dosya i̇şlemleri

PHP, yaygın dosya okuma ve yazma görevlerini basitleştiren pratik fonksiyonlar da sunar. Bu fonksiyonlar, dosya açma, okuma/yazma ve kapama adımlarını tek bir çağrıda birleştirerek kodu daha kısa ve okunabilir hale getirir. Ancak, çok büyük dosyalarla çalışırken veya dosyanın belirli bir bölümünü okuma/yazma gibi karmaşık senaryolarda temel fopen() tabanlı yöntemler daha fazla kontrol sağlar.

file_get_contents() fonksiyonu, bir dosyanın tamamını okuyarak içeriğini bir dize olarak döndürür. Bu, yapılandırma dosyaları, küçük metin dosyaları veya web sayfalarının içeriğini almak gibi durumlar için idealdir.


<?php
$dosya = 'ayarlar.json';
$icerik = file_get_contents($dosya);

if ($icerik !== false) {
    echo "<p>Dosya içeriği:<br><pre>" . htmlspecialchars($icerik) . "</pre></p>";
    // JSON içeriğini işleme
    $ayarlar = json_decode($icerik, true);
    if ($ayarlar) {
        echo "<p>Veritabanı ana bilgisayarı: " . htmlspecialchars($ayarlar['database']['host']) . "</p>";
    }
} else {
    echo "<p>Dosya okunamadı: $dosya</p>";
}
?>
    

Benzer şekilde, file_put_contents() fonksiyonu, bir dizeyi belirtilen bir dosyaya yazar. Dosya yoksa oluşturulur, varsa varsayılan olarak içeriği üzerine yazılır. Bu fonksiyon, ikinci bir parametre olarak bayraklar (flags) alabilir. Örneğin, FILE_APPEND bayrağı, içeriği dosyanın sonuna eklemek için kullanılır.


<?php
$dosya = 'rapor.txt';
$veri = "Günlük rapor: " . date('Y-m-d') . " - İşlemler tamamlandı.\n";

// Mevcut içeriğin üzerine yazar
if (file_put_contents($dosya, $veri) !== false) {
    echo "<p>Rapor dosyasına başarıyla yazıldı.</p>";
} else {
    echo "<p>Rapor dosyasına yazılırken hata oluştu.</p>";
}

// Dosyanın sonuna ekler
$ekVeri = "Ek bilgi: Tüm sistemler çevrimiçi.\n";
if (file_put_contents($dosya, $ekVeri, FILE_APPEND) !== false) {
    echo "<p>Ek veri rapor dosyasına başarıyla eklendi.</p>";
} else {
    echo "<p>Ek veri eklenirken hata oluştu.</p>";
}
?>
    

Bu basitleştirilmiş fonksiyonlar, tek seferlik okuma ve yazma işlemleri için oldukça verimlidir. Ancak, dosyanın sadece bir bölümünü değiştirmek veya çok büyük dosyaları bayt bayt işlemek gibi senaryolarda, fopen() ve ilgili fonksiyonlar daha esnek ve kontrollü bir yaklaşım sunar.

Dosya yükleme i̇şlemleri ve $_FILES süper globali

Web uygulamalarında kullanıcıların dosya yüklemesine izin vermek yaygın bir gereksinimdir. PHP, bu işlemi $_FILES süper global dizisi aracılığıyla kolaylaştırır. Bir dosya yükleme formu oluşturmak için, HTML form etiketine enctype="multipart/form-data" özelliğini eklemek ve dosya giriş alanı için bir type="file" etiketi kullanmak zorunludur.


<!-- index.html -->
<form action="upload.php" method="post" enctype="multipart/form-data">
    <label for="dosya">Yüklenecek dosya:</label>
    <input type="file" name="dosya" id="dosya">
    <br>
    <input type="submit" value="Yükle">
</form>
    

Kullanıcı formu gönderdiğinde, yüklenecek dosya bilgileri PHP tarafında $_FILES dizisinde otomatik olarak kullanılabilir hale gelir. $_FILES['input_name'] yapısı aşağıdaki önemli bilgileri içerir:

  • name: Dosyanın orijinal adı.
  • type: Dosyanın MIME türü (örn. image/jpeg).
  • tmp_name: Sunucuda geçici olarak depolanan dosyanın yolu.
  • error: Yükleme sırasında oluşan hata kodu (0 = başarı).
  • size: Dosyanın boyutu (bayt cinsinden).

Yüklenen geçici dosyayı kalıcı bir konuma taşımak için move_uploaded_file() fonksiyonu kullanılır. Bu fonksiyon, geçici dosyanın gerçekten bir HTTP POST yüklemesi olup olmadığını kontrol ettiği için önemlidir ve doğrudan rename() kullanmaktan daha güvenlidir.


<!-- upload.php -->
<?php
if (isset($_FILES['dosya'])) {
    $yuklenenDosya = $_FILES['dosya'];

    // Hata kontrolü
    if ($yuklenenDosya['error'] === UPLOAD_ERR_OK) {
        $geciciAd = $yuklenenDosya['tmp_name'];
        $orijinalAd = basename($yuklenenDosya['name']); // Dizin ayrıştırma saldırılarını önlemek için

        $hedefDizin = 'uploads/'; // Dosyaların yükleneceği dizin
        $hedefYol = $hedefDizin . $orijinalAd;

        // Dizin yoksa oluştur
        if (!is_dir($hedefDizin)) {
            mkdir($hedefDizin, 0755, true);
        }

        // Dosyayı kalıcı konumuna taşı
        if (move_uploaded_file($geciciAd, $hedefYol)) {
            echo "<p>Dosya başarıyla yüklendi: " . htmlspecialchars($hedefYol) . "</p>";
        } else {
            echo "<p>Dosya taşınırken bir hata oluştu.</p>";
        }
    } else {
        echo "<p>Dosya yüklenirken hata oluştu. Hata kodu: " . $yuklenenDosya['error'] . "</p>";
    }
} else {
    echo "<p>Herhangi bir dosya seçilmedi veya form hatalı.</p>";
}
?>
    

Yukarıdaki örnek temel bir yükleme işlemini gösterse de, güvenlik önlemleri olmadan doğrudan kullanılması ciddi riskler taşır. Bu nedenle, bir sonraki bölümde ele alınacak güvenlik kontrolleri zorunludur.

Güvenlik en iyi uygulamaları

Dosya sistemi operasyonları ve özellikle dosya yükleme, web uygulamaları için en büyük güvenlik risklerinden bazılarını oluşturur. Geliştiricilerin bu riskleri azaltmak için proaktif önlemler alması kritik öneme sahiptir.

  • Giriş doğrulama ve temizleme: Kullanıcılardan gelen tüm girişleri (dosya adları, uzantılar, boyutlar vb.) her zaman doğrulayın ve temizleyin.
    • Dosya boyutu kontrolü: $_FILES['file']['size'] değerini kullanarak, sunucunuzu DoS saldırılarından korumak ve disk alanınızın aşırı tüketilmesini önlemek için maksimum bir dosya boyutu belirleyin.
    • Dosya türü doğrulama: Yalnızca izin verilen dosya türlerinin yüklenmesine izin verin. Hem MIME türünü ($_FILES['file']['type']) hem de dosya uzantısını kontrol edin. MIME türü kolayca taklit edilebilir, bu yüzden uzantı kontrolü de önemlidir. Ayrıca finfo_open() gibi fonksiyonlarla dosyanın gerçek içeriğini kontrol etmek en güvenli yaklaşımdır.
    • Dosya adı temizliği: Dosya adlarından tehlikeli karakterleri (../, \, /, vb.) temizleyin. basename() fonksiyonu, dizin ayrıştırma saldırılarını önlemek için bir dosya yolundan sadece dosya adını güvenli bir şekilde alır.
  • Benzersiz dosya adları oluşturma: Yüklenen dosyaları doğrudan orijinal adlarıyla kaydetmekten kaçının. Bunun yerine, benzersiz bir ad (örneğin, uniqid(), md5(time()) kullanarak veya veritabanından bir ID ile birleştirerek) oluşturun. Bu, dosya çakışmalarını önler ve kötü amaçlı dosyaların mevcut kritik dosyaların üzerine yazılmasını engeller.
  • Yükleme dizini yönetimi:
    • Web kökü dışında depolama: Yüklenen dosyaları web sunucusunun doğrudan erişemeyeceği (web kökü dışındaki) bir dizine kaydedin. Bu, kötü amaçlı bir betiğin doğrudan tarayıcı üzerinden yürütülmesini engeller. Eğer dosyaların sunulması gerekiyorsa, dosyaları PHP betikleri aracılığıyla servis edin ve erişim kontrolleri uygulayın.
    • Dizin izinleri: Yükleme dizinine minimum izinler verin (örneğin, 0755 veya 0775). Asla tam yazma izni (0777) vermeyin, bu güvenlik açıkları yaratır.
  • Kötü amaçlı dosya içeriklerini tarama: Özellikle yürütülebilir dosyaların (PHP, ASP, JS vb.) yüklenmesini engellemek için mümkünse bir antivirüs veya güvenlik tarayıcısı entegre edin. Resim dosyaları gibi görünen dosyaların içine kötü amaçlı kod gizlenebileceğini unutmayın (Polyglot dosyalar).
  • Hata yönetimi: Dosya yükleme ve işleme sırasında oluşabilecek tüm hataları düzgün bir şekilde ele alın ve kullanıcılara genel, bilgilendirici mesajlar gösterin. Detaylı hata mesajlarını asla doğrudan kullanıcılara ifşa etmeyin, bu saldırganlara ipuçları verebilir.

Bu güvenlik önlemleri, dosya sistemi operasyonlarının potansiyel risklerini minimize etmek ve uygulamanızın bütünlüğünü korumak için birleşik bir savunma hattı oluşturur.

Sonuç

PHP’de dosya sistemleriyle çalışmak, web uygulamalarının dinamik olmasını sağlayan temel bir yetenektir. Bu yazı boyunca, fopen(), fread(), fwrite() gibi düşük seviyeli akış fonksiyonlarından, file_get_contents() ve file_put_contents() gibi daha yüksek seviyeli, basitleştirilmiş dosya işleme yöntemlerine kadar çeşitli araçları inceledik. Bu fonksiyonlar, dosya okuma, yazma ve ekleme gibi temel görevler için hayati öneme sahiptir. Ayrıca, kullanıcı dosya yüklemelerini yönetmek için $_FILES süper globalini ve move_uploaded_file() fonksiyonunu detaylandırdık.

Dosya sistemi operasyonlarının gücüne rağmen, güvenlik her zaman en büyük öncelik olmalıdır. Dosya yükleme gibi süreçlerde, giriş doğrulama, dosya adı temizliği, benzersiz adlandırma, web kökü dışında depolama ve doğru dizin izinleri gibi katı güvenlik uygulamaları esastır. Bu önlemler, uygulamanızı kötü amaçlı içeriklerden ve yaygın güvenlik açıklarından korur. Geliştiriciler, hem işlevselliği hem de güvenlik en iyi uygulamalarını derinlemesine anlayarak, etkili ve güvenli dosya yönetimi çözümleri oluşturmalıdır. Güvenlik, geliştirme sürecinin ayrılmaz bir parçasıdır.

Resim Sahibi: Ron Lach
https://www.pexels.com/@ron-lach

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir