Fayl yuklash — $_FILES, move_uploaded_file, xavfsizlik
Muammodan boshlaylik
Foydalanuvchi profil rasmini yuklashi kerak. Oddiy ko'rinadi: formada fayl tanlash maydoni, serverda saqlash. Lekin fayl yuklash — veb-saytlardagi eng xavfli funksiyalardan biri.
Tasavvur qiling: kimdir rasm o'rniga rasm.php faylini yukladi, ichida:
<?php system($_GET['cmd']);
Siz uni uploads/ papkaga saqladingiz. Endi hujumchi sayt.uz/uploads/rasm.php?cmd=... ni ochib, serveringizda istalgan buyruqni bajaradi. Butun sayt, baza, boshqa foydalanuvchilar ma'lumotlari — hammasi uning qo'lida.
Shuning uchun bu darsda faqat "qanday yuklash" emas, "qanday xavfsiz yuklash" ni o'rganamiz.
Forma
<form method="post" enctype="multipart/form-data">
<input type="file" name="rasm" accept="image/*">
<button>Yuklash</button>
</form>
Ikki shart:
method="post"— GET bilan fayl yuborib bo'lmaydienctype="multipart/form-data"— busiz fayl yuborilmaydi, faqat nomi keladi
$_FILES nima beradi
<?php
print_r($_FILES['rasm']);
Array
(
[name] => mening-rasmim.jpg ← foydalanuvchi kompyuteridagi nomi
[full_path] => mening-rasmim.jpg
[type] => image/jpeg ← BRAUZER aytgan tur — ishonib bo'lmaydi!
[tmp_name] => /tmp/phpA1b2C3 ← serverdagi vaqtinchalik fayl
[error] => 0 ← 0 = xato yo'q
[size] => 245760 ← hajmi baytlarda
)
PHP faylni avval vaqtinchalik papkaga saqlaydi. Skript tugashi bilan u o'chiriladi. Saqlab qolish uchun uni move_uploaded_file() bilan ko'chirish kerak.
name va type — foydalanuvchi yuborgan ma'lumot. Ikkalasini ham soxtalashtirish mumkin.
Xato kodlari
| Kod | Konstanta | Ma'nosi |
|---|---|---|
| 0 | UPLOAD_ERR_OK |
Muvaffaqiyatli |
| 1 | UPLOAD_ERR_INI_SIZE |
php.ini dagi upload_max_filesize dan katta |
| 2 | UPLOAD_ERR_FORM_SIZE |
Formadagi MAX_FILE_SIZE dan katta |
| 3 | UPLOAD_ERR_PARTIAL |
Qisman yuklandi |
| 4 | UPLOAD_ERR_NO_FILE |
Fayl tanlanmagan |
Xavfsiz yuklash — to'liq misol
<?php
declare(strict_types=1);
const MAKS_HAJM = 2 * 1024 * 1024; // 2 MB
const RUXSAT = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/webp' => 'webp',
];
$xato = '';
$natija = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$fayl = $_FILES['rasm'] ?? null;
if (!$fayl || $fayl['error'] === UPLOAD_ERR_NO_FILE) {
$xato = "Fayl tanlanmagan";
} elseif ($fayl['error'] !== UPLOAD_ERR_OK) {
$xato = "Yuklashda xato (kod: {$fayl['error']})";
} elseif ($fayl['size'] > MAKS_HAJM) {
$xato = "Fayl 2 MB dan oshmasin";
} else {
// 1. Haqiqiy turini faylning MAZMUNIDAN aniqlash
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($fayl['tmp_name']);
if (!isset(RUXSAT[$mime])) {
$xato = "Faqat JPG, PNG yoki WEBP rasm";
} else {
// 2. Yangi, tasodifiy nom — foydalanuvchi bergan nomni ishlatmaymiz
$kengaytma = RUXSAT[$mime];
$yangiNom = bin2hex(random_bytes(16)) . '.' . $kengaytma;
$manzil = __DIR__ . '/uploads/' . $yangiNom;
// 3. Ko'chirish
if (move_uploaded_file($fayl['tmp_name'], $manzil)) {
$natija = 'uploads/' . $yangiNom;
} else {
$xato = "Faylni saqlab bo'lmadi";
}
}
}
}
?>
<form method="post" enctype="multipart/form-data">
<?php if ($xato): ?><p style="color:red"><?= htmlspecialchars($xato) ?></p><?php endif; ?>
<?php if ($natija): ?><img src="<?= htmlspecialchars($natija) ?>" width="200"><?php endif; ?>
<input type="file" name="rasm" accept="image/jpeg,image/png,image/webp">
<button>Yuklash</button>
</form>
Xavfsizlik qoidalari — nima uchun har biri kerak
1. Turini finfo bilan aniqlang, $_FILES['type'] bilan emas.
type ni brauzer yuboradi — hujumchi .php faylni image/jpeg deb yubora oladi. finfo esa faylning birinchi baytlarini ("sehrli raqamlar") o'qib, haqiqiy turini aniqlaydi.
2. Kengaytmani o'zingiz belgilang.
Foydalanuvchi nomi rasm.jpg.php yoki rasm.php%00.jpg bo'lishi mumkin. Biz kengaytmani aniqlangan MIME turidan olamiz — foydalanuvchi nomidan emas.
3. Nomni tasodifiy yarating.
random_bytes(16) — taxmin qilib bo'lmaydigan nom. Bu ham boshqa fayllarni ustidan yozib yuborishdan (../index.php), ham boshqa foydalanuvchilarning fayllarini sanab chiqishdan himoya qiladi. Asl nomni kerak bo'lsa bazada saqlang.
4. move_uploaded_file ishlating, rename yoki copy emas.
U faylning haqiqatan ham HTTP orqali yuklanganini tekshiradi.
5. Yuklash papkasida PHP bajarilmasin.
Eng yaxshisi — uploads/ ni umuman veb-ildizdan tashqarida saqlash va fayllarni PHP skript orqali berish. Agar ichida bo'lsa, Apache uchun uploads/.htaccess:
php_flag engine off
<FilesMatch "\.(php|phtml|phar)quot;>
Require all denied
</FilesMatch>
Nginx da bu location bloki orqali sozlanadi.
Hajm cheklovlari
PHP ning o'z cheklovlari bor (php.ini):
upload_max_filesize = 2M ← bitta fayl
post_max_size = 8M ← butun so'rov (barcha fayllar + maydonlar)
max_file_uploads = 20 ← bir so'rovda nechta fayl
[!WARNING] Agar so'rov
post_max_sizedan katta bo'lsa — PHP$_POSTva$_FILESni butunlay bo'sh qoldiradi, hech qanday xato kodi bermaydi. "Forma ishlamayapti" degan sirli xatolarning tez-tez uchraydigan sababi.
Bir nechta fayl
<input type="file" name="rasmlar[]" multiple>
$_FILES bu holatda noqulay tuzilmada keladi — har bir xususiyat alohida massiv:
<?php
// $_FILES['rasmlar']['name'][0], $_FILES['rasmlar']['name'][1], ...
$soni = count($_FILES['rasmlar']['name']);
for ($i = 0; $i < $soni; $i++) {
$fayl = [
'name' => $_FILES['rasmlar']['name'][$i],
'tmp_name' => $_FILES['rasmlar']['tmp_name'][$i],
'error' => $_FILES['rasmlar']['error'][$i],
'size' => $_FILES['rasmlar']['size'][$i],
];
// har birini yuqoridagi kabi tekshirish va saqlash
}
Nima uchun shunday?
Nima uchun PHP faylni avval vaqtinchalik papkaga saqlaydi, to'g'ridan-to'g'ri kerakli joyga emas?
Chunki PHP sizning skriptingiz ishga tushishidan oldin butun so'rovni qabul qilib oladi. Bu vaqtda u hali faylni qayerga saqlashni bilmaydi — bu sizning kodingizda yozilgan. Bundan tashqari, vaqtinchalik saqlash xavfsizlik chegarasi: tekshiruvdan o'tmagan fayl hech qachon doimiy joyga tushmaydi va skript tugashi bilan o'z-o'zidan o'chadi.
Xulosa
Eslab qoling:
- Formada
method="post"vaenctype="multipart/form-data"shart $_FILES['x']['error']ni birinchi tekshiring- Turini
finfobilan aniqlang;namevatypega ishonmang - Kengaytma va nomni o'zingiz yarating:
bin2hex(random_bytes(16)) move_uploaded_file()bilan ko'chiring- Yuklash papkasida PHP bajarilmasin — yaxshisi veb-ildizdan tashqarida
Keyingi dars
23-dars: header(), redirect, HTTP status kodlar, JSON javob