При обработке загрузки файлов, в соответствии с PHP официальная документация, имя файла должно быть очищено от обхода каталога и, возможно, от других видов атак:
// basename() may prevent filesystem traversal attacks;
// further validation/sanitation of the filename may be appropriate
$name = basename($_FILES["pictures"]["name"][$key]);
Несмотря на это, я обнаружил, что по умолчанию имя файла уже очищено, когда оно поступает в скрипт PHP.
У меня есть доказательства того, что Apache получает вредоносное имя файла: filename = «../ file.png», в то время как PHP-скрипт вместо этого считывает очищенное имя в переменную $ _FILES.
Низкоуровневый дамп ввода Apache:
mod_dumpio: dumpio_in (data-HEAP):
--------------------------eb8b65b665870e02
Content-Disposition: form-data;
name="attachment";
filename="../file.png" ← [Malicious file name]
Content-Type: image/png
PHP скрипт
echo $_FILES['attachment']['name']; ← [File name already sanitised: 'file.png']
Я обнаружил такое поведение как в модуле Apache, так и в php-fpm при работе PHP с 5.5 до 7.2, и я должен сделать вывод, что интерпретатор PHP выполняет эту очистку перед передачей переменной в сценарий.
Итак, спасибо PHP за то, что сделали санитарию для меня без моего ведома и согласия. тем не мение (и это мой вопрос) поскольку эта функция, насколько я знаю, не документирована, я хотел бы знать критерии / регулярные выражения / алгоритм очистки, чтобы обеспечить ее соответствие моим потребностям.
Вы хотите посмотреть на rfc1867.c
Кажется, это та часть, на которую вы ссылаетесь:
SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
Из комментария видно, что базовое () используется, чтобы избавиться от ложных обратных слешей, которые на самом деле могут быть правильный (Я представляю, возможно «Hello\ World.txt
«?). Но это основано на поведении IE и в комментарии говорится, что он может быть удален в будущем.
Таким образом, вы не можете полагаться на эту «дезинфекцию», чтобы оставаться там.
…
/* The \ check should technically be needed for win32 systems only where
* it is a valid path separator. However, IE in all it's wisdom always sends
* the full path of the file on the user's filesystem, which means that unless
* the user does basename() they get a bogus file name. Until IE's user base drops
* to nill or problem is fixed this code must remain enabled for all systems. */
s = _basename(internal_encoding, filename TSRMLS_CC);
if (!s) {
s = filename;
}
Других решений пока нет …