From 16e16c549056a4acf1e5f2ae628ee1e4eb498995 Mon Sep 17 00:00:00 2001 From: Lain Iwakura Date: Mon, 16 Jun 2025 01:48:41 +0300 Subject: [PATCH] api security --- main/api.php | 25 ++++++++++++++++++++++--- main/db.sql | 5 ++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/main/api.php b/main/api.php index f0874a6..cbe3134 100644 --- a/main/api.php +++ b/main/api.php @@ -5,6 +5,16 @@ header('X-Frame-Options: DENY'); header('X-XSS-Protection: 1; mode=block'); header('Content-Security-Policy: default-src \'self\''); +session_start(); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!isset($_SESSION['csrf_token']) || !isset($_SERVER['HTTP_X_CSRF_TOKEN']) || + $_SESSION['csrf_token'] !== $_SERVER['HTTP_X_CSRF_TOKEN']) { + http_response_code(403); + die(json_encode(['error' => 'Invalid CSRF token'])); + } +} + $config = require 'config.php'; $db = new PDO( "mysql:host={$config['db']['host']};dbname={$config['db']['name']}", @@ -18,7 +28,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $limit = min(max(1, $limit), 100); try { - $stmt = $db->query("SELECT username, message, created_at, signature, is_encrypted FROM messages ORDER BY created_at DESC LIMIT $limit"); + $stmt = $db->prepare("SELECT username, message, created_at, signature, is_encrypted FROM messages ORDER BY created_at DESC LIMIT ?"); + $stmt->execute([$limit]); $messages = $stmt->fetchAll(PDO::FETCH_ASSOC); $filtered_messages = array_map(function($msg) { @@ -45,6 +56,14 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { die(json_encode(['error' => 'Method not allowed'])); } +$ip = $_SERVER['REMOTE_ADDR']; +$stmt = $db->prepare("SELECT COUNT(*) FROM messages WHERE username = ? AND created_at > DATE_SUB(NOW(), INTERVAL 1 MINUTE)"); +$stmt->execute([$ip]); +if ($stmt->fetchColumn() > 10) { + http_response_code(429); + die(json_encode(['error' => 'Too many requests'])); +} + $input = json_decode(file_get_contents('php://input'), true); if (!$input) { http_response_code(400); @@ -56,9 +75,9 @@ $message = $input['message'] ?? ''; $signature = $input['signature'] ?? ''; $is_encrypted = !empty($input['encrypted']) ? 1 : 0; -if (!$username || !$message) { +if (!$username || !$message || strlen($username) > 50 || strlen($message) > 10000) { http_response_code(400); - die(json_encode(['error' => 'Missing required fields'])); + die(json_encode(['error' => 'Invalid input'])); } try { diff --git a/main/db.sql b/main/db.sql index 9482ed4..a73ae5c 100644 --- a/main/db.sql +++ b/main/db.sql @@ -17,6 +17,8 @@ CREATE TABLE users ( is_moderator TINYINT(1) NOT NULL DEFAULT 0, login_attempts INT NOT NULL DEFAULT 0, last_attempt TIMESTAMP NULL, + is_blocked TINYINT(1) NOT NULL DEFAULT 0, + block_reason TEXT, INDEX idx_username (username) ); @@ -24,5 +26,6 @@ CREATE TABLE registrations ( id INT AUTO_INCREMENT PRIMARY KEY, ip VARCHAR(45) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - INDEX idx_ip_created (ip, created_at) + INDEX idx_ip_created (ip, created_at), + INDEX idx_created_at (created_at) ); \ No newline at end of file