From 272c8f9cebe0606b5b0fb6e1b547a2ab95eeedd0 Mon Sep 17 00:00:00 2001 From: merelendor Date: Sat, 30 Nov 2024 07:57:37 +0000 Subject: [PATCH 1/3] security audit fixes: task ID 12, 14, 15: remote X-Forwarded-For headers key lookup, move IP lookup / auth key lookup to web-server generated keys --- api/index.php | 8 ++++---- readme.md | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/api/index.php b/api/index.php index 1451af9..459b58c 100644 --- a/api/index.php +++ b/api/index.php @@ -89,7 +89,7 @@ function checkRequestIsLocal() return true; } - if(strpos($_SERVER['HTTP_X_FORWARDED_FOR'], SELF_IP) > -1) + if($_SERVER[API_SELF_IP_LOOKUP_KEY] === SELF_IP) { return true; } @@ -342,9 +342,9 @@ function setCompanyForUser($ID, $REQ, $replace = false) } } -if($_SERVER['REMOTE_USER'] && strpos($_SERVER['REMOTE_USER'], "Bearer") > -1) +if($_SERVER[API_AUTH_LOOKUP_KEY] && strpos($_SERVER[API_AUTH_LOOKUP_KEY], "Bearer") > -1) { - $token = str_replace("Bearer ", "", $_SERVER['REMOTE_USER']); + $token = str_replace("Bearer ", "", $_SERVER[API_AUTH_LOOKUP_KEY]); try { @@ -2764,7 +2764,7 @@ switch($PARAM_1) { if(checkRequestIsLocal()) { - $token = str_replace("Bearer ", "", $_SERVER['REMOTE_USER']); + $token = str_replace("Bearer ", "", $_SERVER[API_AUTH_LOOKUP_KEY]); $auth = (array) \Bitrix\Main\Web\JWT::decode($token, $secret, ["HS256"]); $user = new \CUser; diff --git a/readme.md b/readme.md index e17506a..138ae2c 100644 --- a/readme.md +++ b/readme.md @@ -6,4 +6,10 @@ SELF_IP - публичный IPv4 адрес сервера MODE_PRODUCTION - включен ли production режим DEBUG_IBLOCK_CYCLE - позволять ли сохранять дамп структуры элемента инфоблока при Add/Update операциях на подверженных рекламе инфоблоках ADVERTISING_IBLOCK_ARRAY - Массив идентификаторов инфоблоков, формата КОНСТАНТА => строковое значение из проекта evolution-advertiser +``` +## Константы, зависящие от метода размещения - за CDN/без, используется ли контейнеризация +### авторизация в методах API осуществляется посредством передачи JWT токена +``` +API_SELF_IP_LOOKUP_KEY - ключ в заголовках http запроса/передаваемых параметрах веб-сервера, валидные значения: REMOTE_ADDR; при использовании CDN: HTTP_CF_CONNECTING_IP или иной ключ, не подверженный подмене первичным отправителем запроса +API_AUTH_LOOKUP_KEY - ключ в заголовках http запроса/передаваемых параметрах веб-сервера, валидные значения: HTTP_AUTHORIZATION, REMOTE_USER ``` \ No newline at end of file From 0e3fe5fd8acc7e634bbf1d52f8b89ab95b6cd23b Mon Sep 17 00:00:00 2001 From: merelendor Date: Sat, 30 Nov 2024 11:32:11 +0000 Subject: [PATCH 2/3] security audit fixes: task ID 12: remove /api/account/token/ method --- api/index.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/index.php b/api/index.php index bf6fabe..79b6b6f 100644 --- a/api/index.php +++ b/api/index.php @@ -2598,12 +2598,14 @@ switch($PARAM_1) { switch($PARAM_2) { + /* DEPRECATED & DISABLED due to security reasons case "token": { print \Bitrix\Main\Web\JWT::encode(["acc_number" => $REQ['acc_number']], $secret, 'HS256', null, null); die(); } break; + */ case "recovery": { From d8e78722e27d419d644eb3e93acfb235ff810978 Mon Sep 17 00:00:00 2001 From: merelendor Date: Sun, 1 Dec 2024 22:01:45 +0000 Subject: [PATCH 3/3] security audit fixes: task ID 12, 16: add XSS sanitizing for FAQ search, site search, CRM requests --- api/index.php | 20 +++++------ bitrix/php_interface/init.php | 33 ++++++++++++------- .../leasing.programs.faq/component.php | 10 ++++-- search/index.php | 1 + 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/api/index.php b/api/index.php index 79b6b6f..0eb54a3 100644 --- a/api/index.php +++ b/api/index.php @@ -10,7 +10,7 @@ use \Bitrix\Main\Context, \Bitrix\Main\Loader, \Bitrix\Iblock; - +$sanitizer = new CBXSanitizer; $httpClient = new \Bitrix\Main\Web\HttpClient(); $PARAM_1 = isset($_REQUEST["PARAM_1"]) ? $_REQUEST["PARAM_1"] : null; @@ -3208,10 +3208,10 @@ switch($PARAM_1) case "calculation": { $payload = json_encode([ - "car_price" => $REQ['car_price'], - "initial_payment" => $REQ['initial_payment'], - "lease_period" => $REQ['lease_period'], - "redemption_payment" => $REQ['redemption_payment'], + "car_price" => sanitize_string($REQ['car_price'], false, false, true), + "initial_payment" => sanitize_string($REQ['initial_payment'], false, false, true), + "lease_period" => sanitize_string($REQ['lease_period'], false, false, true), + "redemption_payment" => sanitize_string($REQ['redemption_payment'], false, false, true), ]); $c = curl_init(); @@ -3237,7 +3237,7 @@ switch($PARAM_1) case "vizitka": { $c = curl_init(); - curl_setopt($c, CURLOPT_URL, API_HOST."/site/GetUserBusinessCard/?guid=".$_REQUEST['guid']); + curl_setopt($c, CURLOPT_URL, API_HOST."/site/GetUserBusinessCard/?guid=".sanitize_string($REQ['guid'], false, false, true)); curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($c, CURLOPT_TIMEOUT, 30); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); @@ -3257,13 +3257,13 @@ switch($PARAM_1) case "preapproval": { $payload = [ - "inn" => $_REQUEST['vat'], + "inn" => sanitize_string($REQ['vat'], false, false, true), ]; $payload_json = json_encode($payload); $c = curl_init(); - curl_setopt($c, CURLOPT_URL, API_HOST."/site/FindClientInDatabase?inn=".$_REQUEST['vat']); + curl_setopt($c, CURLOPT_URL, API_HOST."/site/FindClientInDatabase?inn=".sanitize_string($REQ['vat'], false, false, true)); curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($c, CURLOPT_TIMEOUT, 30); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); @@ -3297,7 +3297,7 @@ switch($PARAM_1) $ar_user = $rs_user->Fetch(); $company_res = CIBlockElement::GetList([ 'id' => 'desc' ], [ 'IBLOCK_ID' => IBLOCK_ID_CLIENTS, 'CODE' => $auth['acc_number'] ], false, []); - + while ($company_element = $company_res->GetNextElement()) { $company_record = $company_element->GetFields(); @@ -3327,7 +3327,7 @@ switch($PARAM_1) $new_feedback = new CIBlockElement; if($new_feedback_id = $new_feedback->Add($ar_new_feedback)) { - + } print json_encode([ diff --git a/bitrix/php_interface/init.php b/bitrix/php_interface/init.php index f31ec0b..25c0c29 100644 --- a/bitrix/php_interface/init.php +++ b/bitrix/php_interface/init.php @@ -231,7 +231,10 @@ function zerof_request($path, $query, $timeout = 900) $check_file = file_get_contents($_SERVER['DOCUMENT_ROOT']."/zerof-500.txt"); if(strpos($check_file, $check) < 0) { - file_put_contents($_SERVER['DOCUMENT_ROOT']."/zerof-500.txt", $path."\n".var_export($query, true)."\n".$http_code." | ".$response."\n\n"); + if(DEBUG_ZEROF_CYCLE) + { + file_put_contents($_SERVER['DOCUMENT_ROOT']."/zerof-500.txt", $path."\n".var_export($query, true)."\n".$http_code." | ".$response."\n\n"); + } print "response with error logged\n"; } @@ -476,7 +479,10 @@ function OnBeforeIBlockElementAddHandler(&$arFields) if(isset($properties['ADVERTISING'])) { - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_add_before_adv_".$arFields['IBLOCK_ID'].".txt", var_export([ "CODE" => $arFields['CODE'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + if(DEBUG_IBLOCK_CYCLE) + { + file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_add_before_adv_".$arFields['IBLOCK_ID'].".txt", var_export([ "CODE" => $arFields['CODE'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + } if(!is_null($arFields['PROPERTY_VALUES']['ADVERTISING'])) { @@ -612,12 +618,18 @@ function OnBeforeIBlockElementUpdateHandler(&$arFields) if(isset($properties['ADVERTISING'])) { - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_1.txt", var_export([ "ID" => $arFields['ID'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + if(DEBUG_ADV_CYCLE) + { + file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_1.txt", var_export([ "ID" => $arFields['ID'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + } if(is_array($arFields['PROPERTY_VALUES'][$properties['ADVERTISING']])) { - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_2.txt", var_export([ "ID" => $arFields['ID'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_values_on_update.txt", var_export($arFields, true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + if(DEBUG_ADV_CYCLE) + { + file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_2.txt", var_export([ "ID" => $arFields['ID'], ], true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_adv_values_on_update.txt", var_export($arFields, true)."\n\n".str_repeat("-", 150)."\n\n", FILE_APPEND); + } if($arFields['PROPERTY_VALUES'][$properties['ADVERTISING_ERIR']][array_keys($arFields['PROPERTY_VALUES'][$properties['ADVERTISING_ERIR']])[0]]['VALUE'] === "") { @@ -728,12 +740,6 @@ function OnBeforeIBlockElement($arFields) if(count($code_matches) == 0) { $arFields["BODY"] .= " ".$code; - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_index_".$arFields['PARAM2'].".txt", "CODE = ".$code." INDEX\n", FILE_APPEND); - } - else - { - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_index_".$arFields['PARAM2'].".txt", "CODE = ".$code." EXISTS\n", FILE_APPEND); - file_put_contents($_SERVER['DOCUMENT_ROOT']."/element_index_".$arFields['PARAM2']."_dump.txt", $arFields["BODY"]."\n".str_repeat("-", 150)."\n\n", FILE_APPEND); } } } @@ -742,4 +748,9 @@ function OnBeforeIBlockElement($arFields) return $arFields; } +function sanitize_string($str, $punctuation = false, $space = false, $replace = false) +{ + return preg_replace("/[^\d". ($space ? "\s" : "") ."\.\_\-@". ($punctuation ? "\"'=+,;«»()&!?%" : "") ."\p{Latin}\p{Cyrillic}]/ui", $replace ? " " : "", $str); +} + ?> \ No newline at end of file diff --git a/local/components/evolution/leasing.programs.faq/component.php b/local/components/evolution/leasing.programs.faq/component.php index b09ffca..5dcf6f0 100644 --- a/local/components/evolution/leasing.programs.faq/component.php +++ b/local/components/evolution/leasing.programs.faq/component.php @@ -19,10 +19,14 @@ if($this->StartResultCache(60*60*24, md5(var_export($_POST, true)))) { if(CModule::IncludeModule('iblock')) { + $sanitizer = new CBXSanitizer; + $arResult = [ 'SECTIONS' => [], ]; - $arResult['SEARCH'] = $_REQUEST['search']; + + $query = sanitize_string($sanitizer->SanitizeHtml($_REQUEST['search']), true, true, true); + $arResult['SEARCH'] = $query; $sort = Array("SORT" => "ASC", "NAME" => "ASC"); $filter = Array("ACTIVE" => "Y", "IBLOCK_ID" => 19); @@ -33,9 +37,9 @@ if($this->StartResultCache(60*60*24, md5(var_export($_POST, true)))) { $section['ITEMS'] = []; $filter = array_merge($filter, [ "SECTION_ID" => $section['ID'] ]); - if(!empty($_REQUEST['search'])) + if(!empty($query)) { - $filter = array_merge($filter, [ "SEARCHABLE_CONTENT" => "%".$_REQUEST['search']."%" ]); + $filter = array_merge($filter, [ "SEARCHABLE_CONTENT" => "%".$query."%" ]); } $res = CIBlockElement::GetList($sort, $filter, false, $options); diff --git a/search/index.php b/search/index.php index 44badd5..65151ab 100644 --- a/search/index.php +++ b/search/index.php @@ -12,6 +12,7 @@ $APPLICATION->SetTitle("Поиск");
IncludeComponent("bitrix:search.page", "evolution", Array( "TAGS_SORT" => "NAME", "TAGS_PAGE_ELEMENTS" => "150",