🔐 Защита: сессия админа + CSRF + белый список расширений (jpg/png/gif/webp/svg/ico) + MIME-проверка + лимит 5MB + безопасное имя файла
⚙️ Функции: загрузка изображений, список файлов, сортировка по дате
✅ Статус: безопасен (только для админов)
<?php
session_start();
if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}
// CSRF-токен
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
require '../includes/db.php';
include '../includes/header.php';
$message = '';
$error = '';
// Обработка загрузки файла
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
// CSRF проверка
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
$error = "Ошибка безопасности: неверный токен";
} else {
$uploadDir = '../upload/';
// Создаем папку upload, если ее нет
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Запрещённые расширения
$blocked_extensions = ['php', 'php3', 'php4', 'php5', 'phtml', 'phps', 'pl', 'py', 'cgi', 'sh', 'exe', 'bat', 'cmd', 'js'];
// Разрешённые расширения (только изображения)
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico'];
$originalName = basename($_FILES['file']['name']);
$fileExt = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
// Проверка расширения
if (!in_array($fileExt, $allowed_extensions)) {
$error = "❌ Запрещённый тип файла. Разрешены только: " . implode(', ', $allowed_extensions);
}
// Проверка MIME-типа (дополнительный слой)
if (empty($error)) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$allowed_mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
if (!in_array($mime_type, $allowed_mimes)) {
$error = "❌ Файл не является изображением. MIME-тип: " . htmlspecialchars($mime_type);
}
}
// Проверка размера (максимум 5MB)
if (empty($error) && $_FILES['file']['size'] > 5 * 1024 * 1024) {
$error = "❌ Файл слишком большой. Максимум 5MB.";
}
// Генерируем безопасное имя файла
if (empty($error)) {
$safe_name = uniqid() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '', $originalName);
$targetPath = $uploadDir . $safe_name;
// Проверка на существование файла
if (file_exists($targetPath)) {
$error = "Файл уже существует. Переименуйте и попробуйте снова.";
} elseif (move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) {
$message = "✅ Файл успешно загружен: " . htmlspecialchars($safe_name);
} else {
$error = "❌ Ошибка при загрузке файла.";
}
}
}
}
// Получаем список файлов с фильтрацией
$uploadDir = '../upload/';
$files = [];
$total_size = 0;
if (file_exists($uploadDir)) {
$scanned = scandir($uploadDir);
foreach ($scanned as $file) {
if ($file !== '.' && $file !== '..') {
$file_path = $uploadDir . $file;
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
$is_image = in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico']);
$files[] = [
'name' => $file,
'size' => filesize($file_path),
'is_image' => $is_image,
'modified' => date('d.m.Y H:i', filemtime($file_path))
];
$total_size += filesize($file_path);
}
}
// Сортируем по дате (новые сверху)
usort($files, function($a, $b) {
return $b['modified'] <=> $a['modified'];
});
}
?>
<link rel="stylesheet" href="/css/adminka.css">
<style>
.alert-info { background: #e3f2fd; padding: 10px; border-radius: 5px; margin: 10px 0; color: #0c5460; }
.alert-error { background: #f8d7da; padding: 10px; border-radius: 5px; margin: 10px 0; color: #721c24; }
.file-list { list-style: none; padding: 0; margin-top: 20px; }
.file-list li { padding: 8px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
.file-list li:hover { background: #f5f5f5; }
.file-size { color: #666; font-size: 12px; }
.file-icon { font-size: 20px; margin-right: 10px; }
.total-size { margin-top: 10px; text-align: right; font-size: 12px; color: #666; }
.allowed-extensions { background: #1e2a3a; padding: 8px 12px; border-radius: 8px; margin: 15px 0; font-size: 13px; color: #00d4ff; }
</style>
</head>
<body>
<div class="container">
<?php include '../includes/admin_menu.php'; ?>
<h2>📁 Загрузка файлов</h2>
<div class="allowed-extensions">
📸 Разрешённые форматы: JPG, JPEG, PNG, GIF, WEBP, SVG, ICO (максимум 5MB)
</div>
<?php if ($message): ?>
<div class="alert-info"><?= htmlspecialchars($message) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert-error"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="form-group">
<label for="file">Выберите изображение:</label>
<input type="file" name="file" id="file" class="form-control" required accept="image/*">
</div>
<button type="submit" class="btn btn-primary">📤 Загрузить</button>
</form>
<h3>📂 Загруженные файлы</h3>
<?php if (empty($files)): ?>
<p>Нет загруженных файлов.</p>
<?php else: ?>
<ul class="file-list">
<?php foreach ($files as $file): ?>
<li>
<div>
<span class="file-icon"><?= $file['is_image'] ? '🖼️' : '📄' ?></span>
<a href="../upload/<?= htmlspecialchars($file['name'], ENT_QUOTES, 'UTF-8') ?>" target="_blank">
<?= htmlspecialchars($file['name'], ENT_QUOTES, 'UTF-8') ?>
</a>
</div>
<div>
<span class="file-size"><?= round($file['size'] / 1024, 1) ?> KB</span>
<span class="file-size"> • <?= $file['modified'] ?></span>
</div>
</li>
<?php endforeach; ?>
</ul>
<div class="total-size">
📦 Всего файлов: <?= count($files) ?> | Общий размер: <?= round($total_size / 1024 / 1024, 2) ?> MB
</div>
<?php endif; ?>
<p style="margin-top: 20px;">
<a href="index.php">← Назад в админку</a>
</p>
</div>
<?php include '../includes/footer.php'; ?>
</body>
</html>
Комментарии
Пока нет комментариев. Будьте первым!