Загрузите несколько файлов через ajax из HTML-элемента ввода файла

Вопрос или проблема

Я пытаюсь загрузить файлы из HTML-формы для множественной загрузки файлов через ajax на сайте WordPress, но получаю ошибку 400, и я не знаю, почему. Мой код основан на этом руководстве.

jQuery:

$(document).on('change','input[type="file"]',function(){

  var fd = new FormData();
  var files_data = $(this); // Поле <input type="file" />

  // Перебираем каждое значение и создаем массив file[], содержащий данные наших файлов.
  $.each($(files_data), function(i, obj) {
      $.each(obj.files,function(j,file){
          fd.append('files[' + j + ']', file);
      })
  });

  fd.append('action', 'file_upload');

  fd.append('nonce', $('#file_upload_nonce').val());

  fd.append('application_id', $(this).closest('.application_question').attr('data-application-id'));

  $.ajax({
      type: 'POST',
      url: '/wp-admin/admin-ajax.php',
      data: fd,
      contentType: false,
      processData: false,
      success: function(response){
        console.log(response);
      }
  });

});

PHP:

function file_upload(){
  // Сначала проверьте nonce
  if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'file_upload' ) ) {
    echo 'Проверка безопасности не прошла.';
  } else {

    $application_id = $_POST['application_id'];

    foreach ( $_FILES['files']['name'] as $f => $name ) {

      move_uploaded_file( $_FILES["files"]["tmp_name"][$f], '/wp-content/supporting-evidence/' . $application_id . "https://wordpress.stackexchange.com/" . $_FILES["files"]["name"][$f] );

    }

  }
  wp_die();
}
add_action('wp_ajax_file_upload','file_upload');

Что я делаю не так?

Дело в том, что вы вышли из системы, и код это не обрабатывает

В коде также есть серьёзная уязвимость, прокрутите вниз, чтобы увидеть детали

HTTP 400 и 0 — это то, что admin-ajax.php возвращает, когда не может найти обработчик для вашего AJAX-запроса.

Обратите внимание, что если бы вы использовали современный REST API вместо устаревшего admin-ajax.php API, он бы сказал вам об этом понятным человеческим языком и 404, как это:

{"code":"rest_no_route","message":"No route was found matching the URL and request method.","data":{"status":404}}

Он также ответил бы 403 запрещено, если бы вы сделали ту же ошибку, что гораздо труднее сделать, используя AJAX с конечной точкой REST API

Если мы взглянем на руководство, мы увидим, где добавляется действие обработчика AJAX:

add_action('wp_ajax_cvf_upload_files', 'cvf_upload_files');
add_action('wp_ajax_nopriv_cvf_upload_files', 'cvf_upload_files'); // Разрешить отправку с фронтенда

Но, если мы взглянем на код в вашем вопросе:

add_action('wp_ajax_file_upload','file_upload');

Ваш код имеет 1 действие, а в руководстве их 2. Вам не хватает версии wp_ajax_nopriv_.

В целом, имейте в виду, что это семилетнее руководство 2015 года, и вам не следует следовать только одному руководству при исследовании вопросов. Официальная документация разработчиков WordPress объясняет это гораздо лучше, и с тех пор, как было написано это руководство, была создана новая современная API для AJAX, называемая REST API.

Дополнительные заметки

  • вы никогда не проверяли, сработала ли move_uploaded_file или нет, она возвращает значение false, если не удалось, вам нужно проверить это, иначе это будет молчалива провал, и вы будете ломать голову, пытаясь понять, почему файлы отсутствуют
  • нужно создать папку!
  • вы не должны предполагать местоположение папки загрузки, руководство, которое вы поделились, использует wp_upload_dir() вместо предположения /wp-content/uploads для этого
  • Что если $_POST['application_id'] содержало бы что-то злонамеренное? например, ../? или myapplicationID/and/this/is/a/subfolder?
  • AJAX происходит каждый раз, когда поле файла меняется, что означает, что если я случайно выберу неправильный файл, он сразу загрузится, и я не смогу его вернуть! Что если я случайно выберу что-то конфиденциальное, что было рядом, и теперь мы оба в беде? Или если я выберу один и тот же файл 5 раз, теперь у вас на сервере 5 копий. Что если я случайно выберу файл резервной копии блюрей на 24 ГБ?
    • в руководстве также есть проверка за это, но в вашем коде ее нет

🚨 Серьезная проблема безопасности в коде 🚨

  • Отсутствует проверка файлов, я мог бы использовать это, чтобы загружать и запускать PHP файлы!!!!
    • руководство, которым вы следуете, использует функцию wp_check_filetype для этого
    • Я мог бы использовать вашу форму для отправки PHP файлов, которые дали бы мне полный контроль над вашим сайтом, а затем просто посетить example.com/wp-content/supporting-evidence/myapplicationID/hacksite.php, это очень опасно!
    • по меньшей мере проверьте расширения файлов, у WordPress есть функции для проверки допустимых типов файлов

Ответ или решение

Для успешной загрузки нескольких файлов на WordPress сайте с использованием AJAX и HTML-формы, важно учесть несколько ключевых аспектов, чтобы избежать ошибок, таких как ошибка 400. Рассмотрим важные моменты в вашем коде и внесем необходимые улучшения.

Проблема с ошибкой 400

Ошибка 400 обычно возникает, когда сервер не может корректно обработать запрос. Это может происходить из-за отсутствия корректной обработки AJAX-запросов. В вашем коде отсутствует действие для обработчика для неаутентифицированных пользователей. Это означает, что пользователи, не войдя в систему, не могут выполнить AJAX-запрос:

add_action('wp_ajax_file_upload', 'file_upload');
add_action('wp_ajax_nopriv_file_upload', 'file_upload'); // Разрешить фронтальную отправку

Ошибки и рекомендации для PHP-кода

  1. Проверка nonce: Убедитесь, что вы проверяете nonce, чтобы защититься от CSRF-атак.

  2. Перемещение загружаемых файлов: Обязательно проверьте результат функции move_uploaded_file() и обработайте возможные ошибки.

  3. Безопасность папки загрузки: Используйте функции WordPress для определения пути загрузки, такие как wp_upload_dir(), чтобы избежать жестко заданного пути.

  4. Очистка пользовательских введенных данных: Обязательно очищайте и проверяйте $_POST['application_id'], чтобы избежать любых угроз безопасности. Например, используйте sanitize_text_field() для удаления недопустимого контента.

  5. Валидация файлов: Используйте функцию wp_check_filetype() для проверки загружаемых типов файлов. Это поможет предотвратить загрузку потенциально опасных файлов, таких как PHP-скрипты.

  6. Обработка ошибок при загрузке: Не забудьте добавить обработку ошибок для функции move_uploaded_file(), чтобы сообщать пользователю о проблемах.

Оптимизированный пример PHP-кода

function file_upload() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'file_upload')) {
        echo 'Security validation failed.';
        wp_die();
    }

    $application_id = sanitize_text_field($_POST['application_id']);
    $upload_dir = wp_upload_dir();
    $upload_path = $upload_dir['basedir'] . '/supporting-evidence/' . $application_id;

    // Создаем папку, если её нет
    if (!file_exists($upload_path)) {
        mkdir($upload_path, 0755, true);
    }

    foreach ($_FILES['files']['name'] as $key => $name) {
        if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
            // Проверка типа файла
            $file_type = wp_check_filetype($name);
            if (isset($file_type['ext']) && in_array($file_type['ext'], ['jpg', 'jpeg', 'png', 'pdf'])) {
                $uploaded_file = $upload_path . '/' . basename($name);
                if (move_uploaded_file($_FILES['files']['tmp_name'][$key], $uploaded_file)) {
                    echo "Файл $name успешно загружен.";
                } else {
                    echo "Ошибка загрузки файла $name.";
                }
            } else {
                echo "Недопустимый файл: $name.";
            }
        } else {
            echo "Ошибка в загрузке файла с индексом: $key.";
        }
    }
    wp_die();
}
add_action('wp_ajax_file_upload', 'file_upload');
add_action('wp_ajax_nopriv_file_upload', 'file_upload'); // Разрешить фронтальную отправку

Объяснение jQuery-кода

Ваш jQuery-код выглядит корректно. Однако вы можете упростить его, избегая создания многократных вызовов:

$(document).on('change', 'input[type="file"]', function() {
    var fd = new FormData();
    var files_data = $(this)[0].files; // Получите массив файлов непосредственно

    $.each(files_data, function(i, file) {
        fd.append('files[]', file); // Добавляем каждый файл
    });

    fd.append('action', 'file_upload');
    fd.append('nonce', $('#file_upload_nonce').val());
    fd.append('application_id', $(this).closest('.application_question').data('application-id'));

    $.ajax({
        type: 'POST',
        url: '/wp-admin/admin-ajax.php',
        data: fd,
        contentType: false,
        processData: false,
        success: function(response) {
            console.log(response);
        },
        error: function(xhr, status, error) {
            console.error(xhr.responseText);
        }
    });
});

Заключение

Убедитесь, что ваш код соответствует рекомендациям по безопасности и проверке, чтобы избежать эксплуатации уязвимостей. Следуя этим шагам и улучшениям, вы сможете успешно загрузить файлы через AJAX на ваш WordPress сайт без ошибок. Также рекомендуется разрабатывать с учетом возможностей REST API, который предлагает более современный и понятный подход к обработке AJAX-запросов.

Оцените материал
Добавить комментарий

Капча загружается...