Cara Mengamankan Fitur Upload File di Website
Fitur upload yang longgar bisa jadi pintu masuk web shell. Langkah mengamankannya, dari validasi tipe file, nama acak, sampai mematikan eksekusi skrip.
Jawaban singkat
Amankan fitur upload dengan membatasi ekstensi lewat allowlist, memeriksa isi file, mengganti nama file secara acak, menyimpannya di luar document root atau mematikan eksekusi skrip di foldernya, membatasi ukuran, dan memindai malware.
Daftar isi
- Bagaimana langkah mengamankan fitur upload?
- 1. Batasi ekstensi dengan allowlist
- 2. Periksa isi file, bukan hanya nama
- 3. Ganti nama file dengan nama acak
- 4. Simpan di luar document root, atau matikan eksekusi skrip
- 5. Batasi ukuran dan jumlah upload
- 6. Sajikan file dengan header yang aman
- 7. Pindai malware dan pantau folder upload
- Lapisan mana yang wajib dan mana yang tambahan?
- Pertanyaan yang sering diajukan
Apakah website-mu punya formulir yang menerima file, misalnya foto profil, bukti transfer, CV pelamar, atau lampiran tiket? Kalau ya, fitur itu termasuk bagian paling berisiko di seluruh aplikasi. Jawaban singkatnya: amankan dengan beberapa lapis sekaligus, karena satu pemeriksaan saja hampir selalu bisa diakali.
Risiko terbesarnya adalah web shell. Penyerang mengunggah file berisi skrip, lalu membukanya lewat browser, dan server menjalankannya. Sejak itu penyerang bisa membaca file, mengubah website, atau memakai server untuk menyerang pihak lain. Daftar kelemahan MITRE mencatat masalah ini sebagai CWE-434, "Unrestricted Upload of File with Dangerous Type".
Bagaimana langkah mengamankan fitur upload?
1. Batasi ekstensi dengan allowlist
Tentukan dengan tegas tipe file apa saja yang benar-benar dibutuhkan, misalnya jpg, jpeg, png, dan pdf, lalu tolak semua yang lain. Hindari pendekatan sebaliknya (blocklist) yang hanya melarang php, karena masih ada ekstensi lain yang bisa dijalankan server, seperti phtml atau phar, tergantung konfigurasinya.
Ambil ekstensi dari bagian paling akhir nama file, ubah ke huruf kecil, lalu cocokkan dengan daftar. Waspadai nama dengan dua ekstensi seperti foto.php.jpg, karena konfigurasi server tertentu bisa salah menafsirkannya.
2. Periksa isi file, bukan hanya nama
Header Content-Type yang dikirim browser bisa dipalsukan dengan mudah. Periksa isi file di server. Di PHP, finfo membaca tanda pengenal di awal file:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['berkas']['tmp_name']);
$izin = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'application/pdf' => 'pdf'];
if (!isset($izin[$mime])) {
http_response_code(400);
exit('Tipe file tidak diizinkan');
}
Untuk gambar, lapisan yang lebih kuat adalah memproses ulang gambar, misalnya dengan GD atau Imagick, lalu menyimpan hasil olahannya. Proses ini membuang data tambahan yang mungkin disisipkan di dalam file gambar.
3. Ganti nama file dengan nama acak
Jangan pernah memakai nama file asli dari pengguna untuk menyimpan file. Buat nama baru, misalnya dengan bin2hex(random_bytes(16)), lalu tambahkan ekstensi dari hasil pemeriksaan di langkah 2. Nama asli boleh disimpan di database untuk ditampilkan, bukan untuk path. Langkah ini sekaligus menutup celah path traversal lewat nama file.
4. Simpan di luar document root, atau matikan eksekusi skrip
Pilihan terbaik adalah menyimpan file di folder yang tidak bisa diakses langsung lewat URL, lalu menyajikannya lewat skrip yang memeriksa hak akses. Bila file memang harus berada di folder publik, matikan eksekusi skrip di folder itu.
Di Apache 2.4, tambahkan di konfigurasi atau .htaccess folder upload:
<FilesMatch "\.(php|phtml|phar)$">
Require all denied
</FilesMatch>
Di Nginx, tambahkan blok berikut sebelum blok location yang meneruskan file PHP ke PHP-FPM, karena lokasi regex dicocokkan sesuai urutan:
location ~* ^/uploads/.*\.(php|phtml|phar)$ {
deny all;
}
5. Batasi ukuran dan jumlah upload
File yang sangat besar atau upload beruntun bisa menghabiskan disk dan memori. Atur batasnya di beberapa tempat sekaligus: upload_max_filesize dan post_max_size di PHP, client_max_body_size di Nginx, serta pemeriksaan ukuran di kode aplikasi. Untuk formulir publik, tambahkan batas frekuensi per alamat IP atau per akun. Cara mengubah batas di WordPress dibahas di cara menaikkan batas upload file WordPress, dan prinsipnya sama untuk menurunkannya.
6. Sajikan file dengan header yang aman
Saat menyajikan file hasil upload, kirim header X-Content-Type-Options: nosniff agar browser tidak menebak tipe file. Untuk file yang tidak perlu ditampilkan langsung, pakai Content-Disposition: attachment agar diunduh, bukan dibuka di browser.
Hati-hati dengan SVG dan HTML. File SVG bisa berisi JavaScript, sehingga bila ditampilkan dari domain utama bisa menjadi celah XSS. Bila tidak benar-benar perlu, jangan izinkan SVG. Bila perlu, sajikan dari domain atau subdomain terpisah.
7. Pindai malware dan pantau folder upload
Untuk file yang dibagikan ke pengguna lain, seperti lampiran di sistem tiket, pindai dengan antivirus seperti ClamAV sebelum diterbitkan. Pantau juga folder upload secara berkala: file dengan ekstensi skrip di folder yang seharusnya hanya berisi gambar adalah tanda bahaya. Panduan menemukan backdoor PHP di hosting membantu bila sudah terlanjur ada file mencurigakan.
Lapisan mana yang wajib dan mana yang tambahan?
| Lapisan | Mencegah | Wajib? |
|---|---|---|
| Allowlist ekstensi | File skrip diunggah | Ya |
| Cek isi dengan finfo | Ekstensi palsu | Ya |
| Nama file acak | Tebak alamat file, path traversal | Ya |
| Di luar document root atau eksekusi dimatikan | Web shell dijalankan | Ya |
| Batas ukuran dan frekuensi | Disk penuh, penyalahgunaan | Ya |
| Header nosniff dan attachment | Browser salah menafsirkan file | Sangat disarankan |
| Proses ulang gambar | Data sisipan di gambar | Disarankan |
| Pindai malware | File berbahaya dibagikan ke pengguna lain | Disarankan untuk file yang dibagikan |
Pertanyaan yang sering diajukan
Apakah cukup mengecek ekstensi file saja?
Tidak. Ekstensi dan header Content-Type mudah dipalsukan. Gabungkan dengan pemeriksaan isi, nama file acak, dan pengaturan server yang mematikan eksekusi skrip di folder upload.
Apakah plugin upload di WordPress sudah aman secara bawaan?
Inti WordPress membatasi tipe file yang boleh diunggah, tetapi plugin formulir atau galeri punya penanganan sendiri dan beberapa pernah punya celah upload. Pastikan plugin selalu diperbarui dan hapus yang tidak dipakai.
Perlukah WAF untuk fitur upload?
WAF bisa menahan sebagian pola serangan yang dikenal, tetapi tidak menggantikan validasi di kode dan pengaturan folder. Anggap sebagai lapisan tambahan.
Bagaimana kalau aplikasi butuh menerima file ZIP?
Periksa setiap path di dalam arsip sebelum diekstrak, batasi jumlah dan total ukuran hasil ekstraksi, dan ekstrak ke folder terpisah yang tidak bisa menjalankan skrip.
Checklist sebelum fitur upload dirilis:
- Ekstensi dibatasi dengan allowlist dan isi file dicek di server.
- File disimpan dengan nama acak, di luar document root atau di folder tanpa eksekusi skrip.
- Batas ukuran diatur di PHP, web server, dan kode aplikasi.
- Header aman dikirim saat file disajikan, dan SVG ditangani khusus.
- Folder upload dipantau berkala.


