Вопрос или проблема
Я пытаюсь загрузить файлы из 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-кода
-
Проверка nonce: Убедитесь, что вы проверяете
nonce
, чтобы защититься от CSRF-атак. -
Перемещение загружаемых файлов: Обязательно проверьте результат функции
move_uploaded_file()
и обработайте возможные ошибки. -
Безопасность папки загрузки: Используйте функции WordPress для определения пути загрузки, такие как
wp_upload_dir()
, чтобы избежать жестко заданного пути. -
Очистка пользовательских введенных данных: Обязательно очищайте и проверяйте
$_POST['application_id']
, чтобы избежать любых угроз безопасности. Например, используйтеsanitize_text_field()
для удаления недопустимого контента. -
Валидация файлов: Используйте функцию
wp_check_filetype()
для проверки загружаемых типов файлов. Это поможет предотвратить загрузку потенциально опасных файлов, таких как PHP-скрипты. -
Обработка ошибок при загрузке: Не забудьте добавить обработку ошибок для функции
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-запросов.