From ae2d835a841510d0cedfa332a2b27ebb2f8e5b5f Mon Sep 17 00:00:00 2001 From: novgorodschi catalin Date: Wed, 5 Aug 2026 10:39:38 +0300 Subject: [PATCH] Important security fix Important security fix --- GameEngine/Building.php | 2 +- GameEngine/Market.php | 2 +- GameEngine/Profile.php | 4 ++-- GameEngine/Session.php | 38 ++++++++++++++++++++++++++++++++++---- GameEngine/Technology.php | 4 ++-- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/GameEngine/Building.php b/GameEngine/Building.php index 507fceb1..c6caa1ec 100755 --- a/GameEngine/Building.php +++ b/GameEngine/Building.php @@ -294,7 +294,7 @@ class Building { $validChecker = ( isset($get['c']) && - $get['c'] == $this->sess->checker + hash_equals((string) $this->sess->checker, (string) ($get['c'] ?? '')) ); // REMOVE / UPGRADE EXISTING diff --git a/GameEngine/Market.php b/GameEngine/Market.php index 76029ec0..29cf7c91 100755 --- a/GameEngine/Market.php +++ b/GameEngine/Market.php @@ -98,7 +98,7 @@ class Market if ( isset($get['t'], $get['a']) && $get['t'] == 1 && - $get['a'] == $session->mchecker && + hash_equals((string) $session->mchecker, (string) ($get['a'] ?? '')) && !isset($get['del']) ) { $session->changeChecker(); diff --git a/GameEngine/Profile.php b/GameEngine/Profile.php index 63946456..1a104194 100755 --- a/GameEngine/Profile.php +++ b/GameEngine/Profile.php @@ -469,7 +469,7 @@ class Profile { exit; } - if ($get['a'] == $session->checker) { + if (hash_equals((string) $session->checker, (string) ($get['a'] ?? ''))) { if ($session->userinfo['sit' . $type] == $get['id']) { $database->updateUserField($session->uid, "sit" . $type, 0, 1); @@ -503,7 +503,7 @@ class Profile { private function removeMeSit($get) { global $database, $session; - if ($get['a'] == $session->checker) { + if (hash_equals((string) $session->checker, (string) ($get['a'] ?? ''))) { $database->removeMeSit($get['id'], $session->uid); $session->changeChecker(); } diff --git a/GameEngine/Session.php b/GameEngine/Session.php index a439624f..6e08e10f 100755 --- a/GameEngine/Session.php +++ b/GameEngine/Session.php @@ -101,6 +101,25 @@ function __construct() { $this->time = time(); if (!isset($_SESSION)) { + /** + * FIX SECURITATE (CSRF): cookie-ul de sesiune nu avea SameSite, deci + * era trimis si la cereri pornite de pe alte site-uri. Panoul de admin + * avea deja 'Strict' (vezi Admin/admin.php); jocul, nu. + * + * Folosim 'Lax', nu 'Strict': cu Strict, un jucator care intra pe link + * din forum sau dintr-un e-mail ar aparea delogat. Lax pastreaza + * navigarea normala si opreste cererile POST venite din alta parte. + */ + if (PHP_VERSION_ID >= 70300) { + session_set_cookie_params(array( + 'lifetime' => 0, + 'path' => '/', + 'secure' => !empty($_SERVER['HTTPS']), + 'httponly' => true, + 'samesite' => 'Lax', + )); + } + session_start(); } @@ -170,8 +189,18 @@ function __construct() { $user_sanitized = $database->escape($user); - $_SESSION['checker'] = $generator->generateRandStr(3); - $_SESSION['mchecker'] = $generator->generateRandStr(5); + /** + * FIX SECURITATE (CSRF): token-urile aveau 3 si 5 caractere. + * + * 3 caractere = 62^3 = 238.328 combinatii, adica se pot incerca toate + * in sub o ora, fara nicio limitare de rata. Cu 32 de caractere ajung + * la 62^32 - imposibil de ghicit. + * + * Generatorul folosea deja random_int(), care e sigur criptografic; + * problema era doar lungimea. + */ + $_SESSION['checker'] = $generator->generateRandStr(32); + $_SESSION['mchecker'] = $generator->generateRandStr(32); $userFields = $database->getUserFields($user_sanitized, "quest, id", 1, true); $_SESSION['qst'] = $userFields["quest"]; @@ -244,8 +273,9 @@ function __construct() { public function changeChecker() { global $generator; - $this->checker = $_SESSION['checker'] = $generator->generateRandStr(3); - $this->mchecker = $_SESSION['mchecker'] = $generator->generateRandStr(5); + // 32 de caractere: vezi explicatia de la generarea initiala. + $this->checker = $_SESSION['checker'] = $generator->generateRandStr(32); + $this->mchecker = $_SESSION['mchecker'] = $generator->generateRandStr(32); } /** diff --git a/GameEngine/Technology.php b/GameEngine/Technology.php index 3cadc95c..7598dc89 100755 --- a/GameEngine/Technology.php +++ b/GameEngine/Technology.php @@ -901,7 +901,7 @@ class Technology { if (!$database->getResearchLock($village->wid)) return; try { $village->researching = $database->getResearching($village->wid, false); - if($this->meetRRequirement($get['a']) && $get['c'] == $session->mchecker) { + if($this->meetRRequirement($get['a']) && hash_equals((string) $session->mchecker, (string) ($get['c'] ?? ''))) { $data = ${'r'.$get['a']}; $time = time() + round(($data['time'] * ($bid22[$building->getTypeLevel(22)]['attri'] / 100))/SPEED); $database->modifyResource($village->wid,$data['wood'],$data['clay'],$data['iron'],$data['crop'],0); @@ -939,7 +939,7 @@ class Technology { $CurrentTech = $ABTech[$type.$get['a']]+$ups; $unit = ($session->tribe-1)*10+intval($get['a']); - if(($ABUpgradesCount < 2 && $session->plus || $ABUpgradesCount == 0) && ($this->getTech($unit) || ($unit % 10) == 1) && ($CurrentTech < $building->getTypeLevel($buildingType)) && $get['c'] == $session->mchecker) { + if(($ABUpgradesCount < 2 && $session->plus || $ABUpgradesCount == 0) && ($this->getTech($unit) || ($unit % 10) == 1) && ($CurrentTech < $building->getTypeLevel($buildingType)) && hash_equals((string) $session->mchecker, (string) ($get['c'] ?? ''))) { global ${'ab'.strval($unit)}; $data = ${'ab'.strval($unit)}; $time = time() + round(($data[$CurrentTech+1]['time'] * ($bidBuilding[$building->getTypeLevel($buildingType)]['attri'] / 100))/SPEED) + ($ABUpgradesCount > 0 ? ($ABUpgrades[$ABUpgradesCount-1]['timestamp'] - time()) + ceil(60/SPEED) : 0);