fileuploadkeamananMenengah4 mnt baca

Upload File yang Aman

Upload tanpa celah: validasi ekstensi, mime asli, batas ukuran, dan nama acak.

Kenapa Upload Adalah Celah Paling Berbahaya?

Upload file berarti mengizinkan orang asing menaruh file di servermu. Kalau validasinya longgar, penyerang mengupload shell.php berkedok gambar, lalu membukanya via browser dan servermu diambil alih. Ini bukan teori: mayoritas pembobolan website kecil berawal dari upload yang tidak divalidasi. Modul ini adalah checklist wajib sebelum upload dipakai di production.

php
<?php
// Contoh 1: validasi berlapis yang benar
$f = $_FILES["foto"];
$errors = [];

// Lapis 1: error code harus OK
if ($f["error"] !== UPLOAD_ERR_OK) {
    die("Upload gagal, kode: " . $f["error"]);
}

// Lapis 2: ukuran maksimal 2MB
if ($f["size"] > 2 * 1024 * 1024) {
    die("File maksimal 2MB.");
}

// Lapis 3: periksa ISI file, bukan klaim browser (MIME asli)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeAsli = $finfo->file($f["tmp_name"]);
$diizinkan = ["image/jpeg" => "jpg", "image/png" => "png"];
if (!isset($diizinkan[$mimeAsli])) {
    die("Hanya JPG/PNG yang diizinkan.");
}

// Lapis 4: nama file acak, ekstensi dari MIME terverifikasi
$namaBaru = bin2hex(random_bytes(16)) . "." . $diizinkan[$mimeAsli];
move_uploaded_file($f["tmp_name"], "uploads/" . $namaBaru);
echo "Tersimpan sebagai: $namaBaru";

Empat lapis ini menutup empat serangan: error code menutup upload rusak, batas ukuran menutup DoS, finfo menutup pemalsuan tipe (membaca isi file, bukan klaim browser), nama acak menutup eksekusi file jahat (penyerang tidak tahu nama filenya) dan tabrakan nama.

php
<?php
// Contoh 2: simpan di luar document root + larang eksekusi PHP
// Struktur: /var/www/uploads (TIDAK bisa diakses via browser langsung)
// Tampilkan via script perantara:
// tampil.php?f=<nama-acak>
$nama = basename($_GET["f"] ?? "");
$path = "/var/www/uploads/" . $nama;

if (!preg_match("/^[a-f0-9]{32}\.(jpg|png)$/", $nama) || !is_file($path)) {
    http_response_code(404);
    exit;
}
header("Content-Type: image/jpeg");
readfile($path);

Menyimpan upload di luar folder yang bisa diakses browser adalah pertahanan terbaik: kalaupun file jahat lolos, ia tidak bisa dibuka via URL. Script perantara memvalidasi nama file dengan regex ketat sebelum menyajikannya.

Jebakan yang Sering Terjadi

Hanya cek ekstensi file. shell.php.jpg atau file PHP yang diganti ekstensi tetap bisa dieksekusi di beberapa konfigurasi server. Ekstensi adalah klaim, finfo adalah fakta. Selalu periksa isi.

Folder uploads bisa dieksekusi. Tambahkan file .htaccess berisi php_flag engine off (Apache) di folder uploads agar file PHP di dalamnya tidak bisa dieksekusi meski lolos validasi. Defense in depth: jangan andalkan satu lapis.

Daftar Periksa Upload Production

Sebelum fitur upload naik ke server asli, pastikan semua ini:

  1. Validasi error code, ukuran, dan MIME via finfo (bukan klaim browser).
  2. Nama file acak (random_bytes), ekstensi dari MIME terverifikasi.
  3. Simpan di luar document root, sajikan via script perantara.
  4. Folder upload tidak bisa eksekusi PHP (.htaccess / konfigurasi server).
  5. Batas ukuran di php.ini sesuai kebutuhan (upload_max_filesize, post_max_size).
  6. Untuk gambar: proses ulang dengan GD/Imagick (resize): ini sekaligus menghancurkan payload jahat yang disisipkan di metadata gambar.
  7. Scan antivirus untuk file dokumen (ClamAV via clamdscan) jika user mengupload file office/PDF.

Poin 6 adalah trik favorit: membuka dan menyimpan ulang gambar menghapus script yang disembunyikan di EXIF/metadata, karena encoder hanya menulis ulang piksel.

Catatan teknis: Fungsi random_bytes(16) menghasilkan 16 byte acak kriptografis yang aman untuk nama file dan token. Jangan pakai rand() atau uniqid() untuk keperluan keamanan: keduanya bisa ditebak.

Tantangan

Uji upload jahat

Buat file teks berisi <?php echo "hacked"; ?> lalu ganti ekstensinya jadi .jpg. Coba upload ke script TANPA validasi vs script DENGAN validasi mime di atas. Catat perbedaannya dan jelaskan kenapa versi aman menolaknya.