From 27c3f1662d407f9aa114f72c0d47ccac0d4aee14 Mon Sep 17 00:00:00 2001 From: Lain Iwakura Date: Thu, 24 Jul 2025 05:15:35 +0300 Subject: [PATCH] first commit --- README | 22 ++++++ RateLimiter.php | 39 ++++++++++ board.php | 187 ++++++++++++++++++++++++++++++++++++++++++++++++ config.php | 13 ++++ index.php | 103 ++++++++++++++++++++++++++ sql/create.sql | 21 ++++++ styles.css | 169 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 554 insertions(+) create mode 100644 README create mode 100644 RateLimiter.php create mode 100644 board.php create mode 100644 config.php create mode 100644 index.php create mode 100644 sql/create.sql create mode 100644 styles.css diff --git a/README b/README new file mode 100644 index 0000000..f9cd392 --- /dev/null +++ b/README @@ -0,0 +1,22 @@ +mkach - анонимный имиджборд + +Установка: +1. Создайте базу данных MySQL +2. Импортируйте sql/create.sql +3. Настройте config.php +4. Создайте папку uploads/ с правами 755 + +Особенности: +- Анонимные посты без регистрации +- Поддержка изображений (jpg, png, gif, webp) +- Rate limiting на основе IP +- Стиль 4chan/2ch +- Ключ доступа для входа +- Автообновление постов + +Безопасность: +- Валидация файлов +- Ограничение размера +- Защита от XSS +- Rate limiting +- Безопасная загрузка файлов \ No newline at end of file diff --git a/RateLimiter.php b/RateLimiter.php new file mode 100644 index 0000000..8f5ddd8 --- /dev/null +++ b/RateLimiter.php @@ -0,0 +1,39 @@ +db = $db; + } + + public function isAllowed($ip, $action = 'post') { + $stmt = $this->db->prepare(' + SELECT COUNT(*) FROM rate_limits + WHERE ip_address = ? AND action_type = ? + AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND) + '); + $stmt->execute([$ip, $action, $this->timeWindow]); + $count = $stmt->fetchColumn(); + + if ($count >= $this->maxRequests) { + return false; + } + + $stmt = $this->db->prepare(' + INSERT INTO rate_limits (ip_address, action_type) VALUES (?, ?) + '); + $stmt->execute([$ip, $action]); + + return true; + } + + public function cleanup() { + $stmt = $this->db->prepare(' + DELETE FROM rate_limits + WHERE created_at < DATE_SUB(NOW(), INTERVAL ? SECOND) + '); + $stmt->execute([$this->timeWindow]); + } +} \ No newline at end of file diff --git a/board.php b/board.php new file mode 100644 index 0000000..fa15f90 --- /dev/null +++ b/board.php @@ -0,0 +1,187 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + die('Connection failed'); +} + +require_once 'RateLimiter.php'; +$rateLimiter = new RateLimiter($db); +$rateLimiter->cleanup(); + +$ip = $_SERVER['REMOTE_ADDR']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!$rateLimiter->isAllowed($ip)) { + $error = 'Слишком много запросов'; + } else { + $message = trim($_POST['message'] ?? ''); + $file = $_FILES['file'] ?? null; + + if ($message || ($file && $file['error'] === UPLOAD_ERR_OK)) { + $postId = sprintf('%06d', mt_rand(1, 999999)); + + $fileName = null; + $fileSize = null; + $fileType = null; + + if ($file && $file['error'] === UPLOAD_ERR_OK) { + $fileExt = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + + if (!in_array($fileExt, $config['allowed_types'])) { + $error = 'Неподдерживаемый тип файла'; + } elseif ($file['size'] > $config['max_file_size']) { + $error = 'Файл слишком большой'; + } else { + $uploadDir = $config['upload_path']; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + $fileName = $postId . '.' . $fileExt; + $filePath = $uploadDir . $fileName; + + if (move_uploaded_file($file['tmp_name'], $filePath)) { + $fileSize = $file['size']; + $fileType = $fileExt; + } else { + $error = 'Ошибка загрузки файла'; + } + } + } + + if (empty($error)) { + $stmt = $db->prepare(' + INSERT INTO posts (post_id, message, file_name, file_size, file_type, ip_address) + VALUES (?, ?, ?, ?, ?, ?) + '); + $stmt->execute([$postId, $message, $fileName, $fileSize, $fileType, $ip]); + + header('Location: board.php'); + exit; + } + } else { + $error = 'Введите сообщение или загрузите файл'; + } + } +} + +if (isset($_GET['logout'])) { + session_destroy(); + header('Location: index.php'); + exit; +} + +try { + $stmt = $db->query('SELECT * FROM posts ORDER BY created_at DESC LIMIT 100'); + $posts = $stmt->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + die('Database error'); +} +?> + + + + + + mkach + + + +
+
+

mkach

+ +
+ +
+ +
+ + +
+ +
+
+ + +
+ + +
+ + File + +
+ + ( KB) +
+
+ + + +
+ +
+ +
+
+ +
+
+
+ +
+
+ + +
+
+
+
+ + + + \ No newline at end of file diff --git a/config.php b/config.php new file mode 100644 index 0000000..9d61563 --- /dev/null +++ b/config.php @@ -0,0 +1,13 @@ + [ + 'host' => 'localhost', + 'name' => 'mkach', + 'user' => 'mkach', + 'pass' => 'your_password' + ], + 'access_key' => 'mkalwaysthebest1337', + 'upload_path' => 'uploads/', + 'max_file_size' => 5242880, + 'allowed_types' => ['jpg', 'jpeg', 'png', 'gif', 'webp'] +]; \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..d5831d8 --- /dev/null +++ b/index.php @@ -0,0 +1,103 @@ + + + + + + + mkach + + + +
+

mkach

+
+
+ +
+ +
+ +
+ +
+ + +