Добавлено логгирование загрузок файлов
This commit is contained in:
@@ -236,7 +236,32 @@ function _commentSettingsGet($param = '')
|
||||
// Возвращаем количество комментариев для запрашиваемого документа
|
||||
return $comments[$document_id];
|
||||
}
|
||||
/**
|
||||
* Логирование подозрительной активности при загрузке файлов
|
||||
*/
|
||||
private function _logUploadSecurity($status, $file_info, $context = [])
|
||||
{
|
||||
$log_dir = BASE_DIR . '/modules/comment/logs/';
|
||||
if (!is_dir($log_dir)) @mkdir($log_dir, 0775, true);
|
||||
|
||||
$log_file = $log_dir . 'security_upload.log';
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||
$user_id = $_SESSION['user_id'] ?? 0;
|
||||
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'none';
|
||||
|
||||
// Формируем строку контекста: ID сообщения, имя автора и т.д.
|
||||
$ctx_info = "";
|
||||
if (!empty($context)) {
|
||||
foreach ($context as $key => $val) {
|
||||
$ctx_info .= " | $key: $val";
|
||||
}
|
||||
}
|
||||
|
||||
$message = "[$date] IP: $ip | UID: $user_id{$ctx_info} | STATUS: $status | FILE: {$file_info['name']} | MIME: {$file_info['mime']} | SIZE: {$file_info['size']} bytes | UA: $user_agent\n";
|
||||
|
||||
@file_put_contents($log_file, $message, FILE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Следующие методы описывают работу модуля в Публичной части сайта.
|
||||
@@ -659,17 +684,34 @@ function commentPostFormShow($tpl_dir)
|
||||
$mime = finfo_file($finfo, $file['tmp_name']);
|
||||
finfo_close($finfo);
|
||||
|
||||
// Проверки расширения и безопасности
|
||||
$is_allowed_ext = in_array($ext, $allowed_extensions);
|
||||
$is_dangerous = in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'pl', 'cgi']);
|
||||
// Определяем списки расширений для разной логики проверок
|
||||
$img_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
|
||||
$is_image_ext = in_array($ext, $img_exts);
|
||||
|
||||
// Условие: расширение разрешено, файл не опасен и проходит по размеру
|
||||
if ($is_allowed_ext && !$is_dangerous && $file['size'] > 0 && $file['size'] <= $max_file_size_bytes)
|
||||
// 1. Проверки расширения и базовой безопасности
|
||||
$is_allowed_ext = in_array($ext, $allowed_extensions);
|
||||
$is_dangerous = in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'pl', 'cgi', 'html', 'js']);
|
||||
|
||||
// 2. Проверка на "лже-картинку" (только если расширение из списка изображений)
|
||||
$mime_is_valid = true;
|
||||
if ($is_image_ext && strpos($mime, 'image/') !== 0) {
|
||||
$mime_is_valid = false; // Попытка просунуть скрипт под видом картинки
|
||||
}
|
||||
|
||||
// 3. Защита от опасных MIME-типов (даже если админ разрешил такое расширение)
|
||||
$is_dangerous_mime = in_array($mime, [
|
||||
'text/php', 'text/x-php', 'application/x-php',
|
||||
'application/x-httpd-php', 'application/x-executable',
|
||||
'text/html', 'text/javascript'
|
||||
]);
|
||||
|
||||
// Итоговое условие: разрешено в админке + не опасно + валидный MIME + размер
|
||||
if ($is_allowed_ext && !$is_dangerous && !$is_dangerous_mime && $mime_is_valid && $file['size'] > 0 && $file['size'] <= $max_file_size_bytes)
|
||||
{
|
||||
// 1. Берем имя
|
||||
$original_basename = pathinfo($file['name'], PATHINFO_FILENAME);
|
||||
|
||||
// 2. Очищаем (БЕЗ $this->, просто вызываем функцию)
|
||||
// 2. Очищаем
|
||||
$clean_name = prepare_fname($original_basename);
|
||||
|
||||
// 3. Если пусто - дефолт
|
||||
@@ -685,6 +727,39 @@ if ($is_allowed_ext && !$is_dangerous && $file['size'] > 0 && $file['size'] <= $
|
||||
$processed_hashes[] = $file_hash;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1. Формируем причину отказа
|
||||
$reason = "ОТКАЗ ПРИ СОЗДАНИИ КОММЕНТАРИЯ:";
|
||||
if (!$is_allowed_ext) $reason .= " Запрещенное расширение($ext);";
|
||||
if ($is_dangerous) $reason .= " Опасный формат файла;";
|
||||
if ($is_dangerous_mime) $reason .= " Вредоносный тип контента($mime);";
|
||||
if (!$mime_is_valid) $reason .= " Обнаружена подмена (Fake Image);";
|
||||
if ($file['size'] > $max_file_size_bytes) $reason .= " Файл слишком большой;";
|
||||
if ($file['size'] <= 0) $reason .= " Пустой файл;";
|
||||
|
||||
// 2. Собираем контекст (кто, куда и под каким именем писал)
|
||||
// Берём данные из $_POST, так как запись в БД ещё не создана
|
||||
$context = [
|
||||
'AUTHOR' => $_POST['comment_author_name'] ?? 'Неизвестно',
|
||||
'EMAIL' => $_POST['comment_author_email'] ?? '-',
|
||||
'DOC_ID' => (int)($_POST['doc_id'] ?? 0)
|
||||
];
|
||||
|
||||
// 3. Записываем в лог с контекстом
|
||||
$this->_logUploadSecurity($reason, [
|
||||
'name' => $file['name'],
|
||||
'mime' => $mime,
|
||||
'size' => $file['size']
|
||||
], $context);
|
||||
|
||||
// 4. Защита от дублей в логе (если один и тот же файл пришел дважды)
|
||||
$processed_hashes[] = $file_hash;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,6 +952,8 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
// Получаем текущие файлы из БД в массив
|
||||
$current_files = !empty($row['comment_file']) ? explode(',', $row['comment_file']) : [];
|
||||
|
||||
$processed_hashes = []; // Для защиты лога от дубликатов
|
||||
|
||||
// --- Обработка выборочного удаления ---
|
||||
if (!empty($_POST['delete_files'])) {
|
||||
$files_to_remove = explode(',', $_POST['delete_files']);
|
||||
@@ -919,46 +996,81 @@ if (!$can_upload && isset($_FILES['comment_image'])) {
|
||||
|
||||
// Перебираем загруженные файлы
|
||||
foreach ($_FILES['comment_image']['name'] as $i => $fname) {
|
||||
if ($_FILES['comment_image']['error'][$i] == UPLOAD_ERR_OK) {
|
||||
|
||||
$tmp_name = $_FILES['comment_image']['tmp_name'][$i];
|
||||
$file_ext = strtolower(pathinfo($fname, PATHINFO_EXTENSION));
|
||||
$file_size = $_FILES['comment_image']['size'][$i];
|
||||
|
||||
// Проверка MIME-типа
|
||||
if ($_FILES['comment_image']['error'][$i] == UPLOAD_ERR_OK) {
|
||||
|
||||
$tmp_name = $_FILES['comment_image']['tmp_name'][$i];
|
||||
$file_size = $_FILES['comment_image']['size'][$i];
|
||||
$file_ext = strtolower(pathinfo($fname, PATHINFO_EXTENSION));
|
||||
|
||||
// 1. Проверка MIME-типа и хэша
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $tmp_name);
|
||||
finfo_close($finfo);
|
||||
|
||||
// Проверки расширения и опасных файлов
|
||||
$is_allowed_ext = in_array($file_ext, $allowed_extensions);
|
||||
$is_dangerous = in_array($file_ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'pl', 'cgi']);
|
||||
$file_hash = md5_file($tmp_name);
|
||||
if (in_array($file_hash, $processed_hashes)) continue;
|
||||
|
||||
if ($is_allowed_ext && !$is_dangerous && $file_size > 0 && $file_size <= ($max_kb * 1024))
|
||||
// 2. Логика безопасности
|
||||
$img_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
|
||||
$is_image_ext = in_array($file_ext, $img_exts);
|
||||
$is_allowed_ext = in_array($file_ext, $allowed_extensions);
|
||||
$is_dangerous = in_array($file_ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'pl', 'cgi', 'html', 'js']);
|
||||
|
||||
$mime_is_valid = true;
|
||||
if ($is_image_ext && strpos($mime, 'image/') !== 0) {
|
||||
$mime_is_valid = false;
|
||||
}
|
||||
|
||||
$is_dangerous_mime = in_array($mime, [
|
||||
'text/php', 'text/x-php', 'application/x-php',
|
||||
'application/x-httpd-php', 'application/x-executable',
|
||||
'text/html', 'text/javascript'
|
||||
]);
|
||||
|
||||
// 3. Условие загрузки
|
||||
if ($is_allowed_ext && !$is_dangerous && !$is_dangerous_mime && $mime_is_valid && $file_size > 0 && $file_size <= ($max_kb * 1024))
|
||||
{
|
||||
if (!is_dir($upload_dir)) @mkdir($upload_dir, 0775, true);
|
||||
|
||||
$original_basename = pathinfo($fname, PATHINFO_FILENAME);
|
||||
$clean_name = prepare_fname($original_basename);
|
||||
if (empty($clean_name)) $clean_name = 'file';
|
||||
|
||||
// Получаем оригинальное имя файла без расширения
|
||||
$original_basename = pathinfo($fname, PATHINFO_FILENAME);
|
||||
|
||||
// Очищаем имя
|
||||
$clean_name = prepare_fname($original_basename);
|
||||
|
||||
// Защита от пустого имени
|
||||
if (empty($clean_name)) {
|
||||
$clean_name = 'file';
|
||||
}
|
||||
|
||||
// Формируем новое имя: чистое_имя + время + расширение
|
||||
$new_file_name = $clean_name . '_' . time() . '.' . $file_ext;
|
||||
|
||||
$new_file_name = $clean_name . '_' . time() . '.' . $file_ext;
|
||||
|
||||
if (move_uploaded_file($tmp_name, $upload_dir . $new_file_name)) {
|
||||
$current_files[] = $new_file_name;
|
||||
$processed_hashes[] = $file_hash;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 4. ЛОГИРОВАНИЕ (с контекстом комментария)
|
||||
$reason = "ОТКАЗ ПРИ РЕДАКТИРОВАНИИ КОММЕНТАРИЯ:";
|
||||
if (!$is_allowed_ext) $reason .= " Запрещенное расширение($file_ext);";
|
||||
if ($is_dangerous) $reason .= " Опасный формат файла;";
|
||||
if ($is_dangerous_mime) $reason .= " Вредоносный тип контента($mime);";
|
||||
if (!$mime_is_valid) $reason .= " Обнаружена подмена (Fake Image);";
|
||||
if ($file_size > ($max_kb * 1024)) $reason .= " Файл слишком большой;";
|
||||
|
||||
$context = [
|
||||
'COMMENT_ID' => $comment_id,
|
||||
'AUTHOR_DB' => $row['comment_author_name'],
|
||||
'CURRENT_UID' => $user_id
|
||||
];
|
||||
|
||||
$this->_logUploadSecurity($reason, [
|
||||
'name' => $fname,
|
||||
'mime' => $mime,
|
||||
'size' => $file_size
|
||||
], $context);
|
||||
|
||||
$processed_hashes[] = $file_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Собираем итоговую строку файлов для БД через запятую
|
||||
|
||||
6
logs/.htaccess
Normal file
6
logs/.htaccess
Normal file
@@ -0,0 +1,6 @@
|
||||
# Запрещаем доступ к файлам логов извне (через браузер)
|
||||
Order Deny,Allow
|
||||
Deny from all
|
||||
|
||||
# Отключаем листинг директории
|
||||
Options -Indexes
|
||||
Reference in New Issue
Block a user