Простейшая проверка файла при загрузке или «Я Вам не верю!»

Март 16th, 2011 Рубрики: PHP coubertin.cz www.colvillewoodworking.com

-rw-r-----
Несколько правил которые необходимо соблюдать:

  • 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>&rarr;</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

Теги: , ,

4 комментария к “Простейшая проверка файла при загрузке или «Я Вам не верю!»”

  1. Живой
    Март 17th, 2011 at 00:01
    1

    Молодец 😉 Это клёво, делится опытом, искать правильные решения!
    Небольшое замечание по коду, global — это плохо.

    [Ответить]

    ZekMan Reply:

    Есть предложение чем заменить? Таки глобальная переменная используется только для хранения ошибок…

    [Ответить]

  2. NFL
    Ноябрь 7th, 2011 at 19:03
    2

    Таки, наверное, ООП? Хранить сие дело в члене класса/сессии, а не в глобальной переменной

    [Ответить]

    ZekMan Reply:

    Можно и так, только все равно ошибки надо передавать в другую функцию

    [Ответить]

Написать комментарий