Простейшая проверка файла при загрузке или «Я Вам не верю!»
Несколько правил которые необходимо соблюдать:
- 1. НИКОГДА НЕ ДОВЕРЯЙТЕ ПОЛЬЗОВАТЕЛЯМ
- 2. Файлы никогда не должны исполнятся на сервере ( 644 )
- 3. Нельзя доверять $_FILES[][‘type’]
Если коротко то недавний холивар в php@conference.jabber.ru на тему загрузок файлов заставил меня пересмотреть мой алгоритм проверки файлов. Исходом явилось то что я написал более или менее нормальную функцию проверки файлов. Но не идеальную, так что не пренебрегайте настройкой прав доступа на загружаемые файлы 😉
Все довольно просто и умещается на 74 строчек, хотя все зависит только от количества разрешенных к загрузке файлов, итак:
upload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?php include_once("function.php"); if (@$_POST['upload_file']) { if ($_FILES['upload_file']['error'] === UPLOAD_ERR_OK) { if (file_check($_FILES)) { copy($_FILES['upload_file']['tmp_name'],"file/".$_FILES['upload_file']['name']); } } else { $messages[] = 'При загрузке произошла ошибка, возможно вы не выбрали файл'; } } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title>Safe Upload File</title> </head> <body> <?php if (isset($messages)) { displayErrors($messages); } ?> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="upload_file" id="" /> <input type="submit" value="Upload" name="upload_file" /> </form> </body> </html> |
function.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?php # Вывод сообщений об ошибках function displayErrors($messages) { global $messages; print("<b>Ошибочка вышла!</b><br/>"); foreach($messages as $msg){ print("<b>→</b> $msg<br />"); } } function file_check($_FILES) { global $messages; $expansion = strtolower(pathinfo($_FILES['upload_file']['name'], PATHINFO_EXTENSION)); $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $_FILES['upload_file']['tmp_name']); $allow_expansion = array ( "bmp" => "image/bmp", "gif" => "image/gif", "ief" => "image/ief", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "jpe" => "image/jpeg", "png" => "image/png" ); if ($mime !== $_FILES['upload_file']['type']) { $messages[] = 'Ваш файл не прошел проверку'; return false; } else { foreach ($allow_expansion as $key => $value) { if($value == $mime){ if ($key == $expansion) { return true; } } } $messages[] = 'Ваш файл не прошел проверку'; } } ?> |
Начнем пожалуй с upload.php. Для начала подключаем файл с функциями. По нажатию на кнопочку делаем проверку на то что был выбран файл ( строка 4 ) и если все нормально на пятой строке проверяем какой результат вернула функция file_check. Ну и в конце на 21-23 строчке выводим ошибки если таковые есть.
Ну и самое интересное в function.php. Первой идет функция вывода ошибок, ее я использовал и раньше, нас интересует проверка.
1. Мы получаем расширение файла из $_FILES ( строка 15 ) и сразу переводим его в нижний регистр ( вдруг было в верхнем? )
2. Далее мы получаем mime type, но не из $_FILES[‘upload_file’][‘type’] а из самого файла, на случай если нам таки подсунули $_FILES.
3. Делаем массив разрешенных mime type для загрузки ( позже дам ссылки на ресурсы где можно подсмотреть какие они бывают еще ).
4. Сравниваем полученный нами mime type с тем что пришел от пользователя в $_FILES и если они разные сразу даем сообщение о ошибке и возвращаем false
5. Проходим по массиву разрешенных разрешений и ищем совпадении расширения файла с mime type в нашем массиве.
6. Если все хорошо то возвращаем true — проверка прошла успешно.
Следует сказать так же что если у Вас разрешена загрузка только изображений то следует так же проверять это функцией getimagesize() (если изображение то возвращается массив).
Ну и напоследок довольно большой список mime type
Молодец 😉 Это клёво, делится опытом, искать правильные решения!
Небольшое замечание по коду, global — это плохо.
[Ответить]
ZekMan Reply:
Март 17th, 2011 at 08:45
Есть предложение чем заменить? Таки глобальная переменная используется только для хранения ошибок…
[Ответить]
Таки, наверное, ООП? Хранить сие дело в члене класса/сессии, а не в глобальной переменной
[Ответить]
ZekMan Reply:
Ноябрь 7th, 2011 at 19:24
Можно и так, только все равно ошибки надо передавать в другую функцию
[Ответить]