Skip to content

Commit fc57cfd

Browse files
committed
✨ Refactor CAPTCHA to use browser fingerprinting and SHA-256 hashing for improved security
- Replace `$_SERVER['REMOTE_ADDR']` with `erLhcoreClassIPDetect::getIP()` for consistent IP resolution across proxy configurations - Introduce `erLhcoreClassIPDetect::getFingerprint()` which combines multiple HTTP headers (User-Agent, Accept-Language, Accept-Encoding, Accept-Charset) for a more robust client identifier - Upgrade hashing algorithm from SHA-1 to SHA-256 for all CAPTCHA-related tokens and validation - Apply changes across chat validator, CAPTCHA module, widget settings, FAQ, and voting modules
1 parent a7d5531 commit fc57cfd

8 files changed

Lines changed: 41 additions & 16 deletions

File tree

lhc_web/lib/core/lhchat/lhchatvalidator.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ public static function validateStartChat(& $inputForm, & $start_data_fields, & $
266266
if (erLhcoreClassModelChatConfig::fetch('session_captcha')->current_value == 1) {
267267
// Start session if required only
268268
$currentUser = erLhcoreClassUser::instance();
269-
$hashCaptcha = isset($_SESSION[$_SERVER['REMOTE_ADDR']]['form']) ? $_SESSION[$_SERVER['REMOTE_ADDR']]['form'] : null;
269+
$hashCaptcha = isset($_SESSION[erLhcoreClassIPDetect::getIP()]['form']) ? $_SESSION[erLhcoreClassIPDetect::getIP()]['form'] : null;
270270
$nameField = 'captcha_'.$hashCaptcha;
271271
$validationFields[$nameField] = new ezcInputFormDefinitionElement( ezcInputFormDefinitionElement::OPTIONAL, 'string' );
272272
} else {
@@ -278,7 +278,7 @@ public static function validateStartChat(& $inputForm, & $start_data_fields, & $
278278
$captchaString = $additionalParams['payload_data']['tscaptcha'];
279279
}
280280

281-
$nameField = 'captcha_'.sha1(erLhcoreClassIPDetect::getIP().$captchaString.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
281+
$nameField = 'captcha_'. hash('sha256', erLhcoreClassIPDetect::getFingerprint() . $captchaString . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
282282

283283
$validationFields[$nameField] = new ezcInputFormDefinitionElement( ezcInputFormDefinitionElement::OPTIONAL, 'string' );
284284
}
@@ -332,7 +332,7 @@ public static function validateStartChat(& $inputForm, & $start_data_fields, & $
332332
$Errors['captcha'] = $baseError . ' Security token is missing.';
333333
} elseif ($form->$nameField < time() - 1800) {
334334
$Errors['captcha'] = $baseError . ' Security token has expired.';
335-
} elseif ($hashCaptcha != sha1($_SERVER['REMOTE_ADDR'].$form->$nameField.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))) {
335+
} elseif ($hashCaptcha != hash('sha256', erLhcoreClassIPDetect::getFingerprint() . $form->$nameField . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))) {
336336
$Errors['captcha'] = $baseError . ' Security validation failed.';
337337
}
338338
} else {

lhc_web/lib/core/lhcore/lhipdetect.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@ public static function getIP()
2222
return $_SERVER["REMOTE_ADDR"];
2323
}
2424

25+
public static function getFingerprint()
26+
{
27+
$headers = array(
28+
'HTTP_USER_AGENT',
29+
'HTTP_ACCEPT_LANGUAGE',
30+
'HTTP_ACCEPT_ENCODING',
31+
'HTTP_ACCEPT_CHARSET',
32+
);
33+
34+
$parts = array();
35+
foreach ($headers as $header) {
36+
if (isset($_SERVER[$header]) && $_SERVER[$header] !== '') {
37+
$parts[] = $_SERVER[$header];
38+
}
39+
}
40+
41+
$fingerprint = '';
42+
if (!empty($parts)) {
43+
$fingerprint = sha1(implode('|', $parts));
44+
}
45+
46+
return $fingerprint;
47+
}
48+
49+
2550
public static function getServerAddress()
2651
{
2752
if (array_key_exists('SERVER_ADDR', $_SERVER))

lhc_web/modules/lhcaptcha/captchastring.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
header('Cache-Control: post-check=0, pre-check=0', false );
99
header('Pragma: no-cache' );
1010

11-
$hash = sha1(erLhcoreClassIPDetect::getIP() . $Params['user_parameters']['timets'] . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
11+
$hash = hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $Params['user_parameters']['timets'] . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
1212

1313
if ( (time()-$Params['user_parameters']['timets']) > 600 || (time()-($Params['user_parameters']['timets'] - 600)) < 0) {
1414
echo json_encode(array('result' => 'false'));
@@ -19,7 +19,7 @@
1919
// Start session if required only
2020
$currentUser = erLhcoreClassUser::instance();
2121

22-
$_SESSION[$_SERVER['REMOTE_ADDR']][$Params['user_parameters']['captcha_name']] = $hash;
22+
$_SESSION[erLhcoreClassIPDetect::getIP()][$Params['user_parameters']['captcha_name']] = $hash;
2323
}
2424

2525
echo json_encode(array('result' => $hash, 'ip' => erLhcoreClassIPDetect::getIP()));

lhc_web/modules/lhchat/readoperatormessage.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@
188188
if (erLhcoreClassModelChatConfig::fetch('session_captcha')->current_value == 1) {
189189
// Start session if required only
190190
$currentUser = erLhcoreClassUser::instance();
191-
$hashCaptcha = isset($_SESSION[$_SERVER['REMOTE_ADDR']]['form']) ? $_SESSION[$_SERVER['REMOTE_ADDR']]['form'] : null;
192-
$nameField = 'captcha_'.$hashCaptcha;
191+
$hashCaptcha = isset($_SESSION[erLhcoreClassIPDetect::getIP()]['form']) ? $_SESSION[erLhcoreClassIPDetect::getIP()]['form'] : null;
192+
$nameField = 'captcha_'. $hashCaptcha;
193193
} else {
194194
// Captcha stuff
195-
$nameField = 'captcha_'.sha1(erLhcoreClassIPDetect::getIP().$_POST['tscaptcha'].erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
195+
$nameField = 'captcha_'. hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $_POST['tscaptcha'] . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
196196
}
197197

198198
$validationFields[$nameField] = new ezcInputFormDefinitionElement( ezcInputFormDefinitionElement::OPTIONAL, 'string' );

lhc_web/modules/lhchat/start.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189

190190
$ts = time();
191191
$tpl->set('captcha',array(
192-
'hash' => sha1(erLhcoreClassIPDetect::getIP() . $ts . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' )),
192+
'hash' => hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $ts . erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' )),
193193
'ts' => $ts
194194
));
195195

lhc_web/modules/lhfaq/faqwidget.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@
203203
$nameField = 'captcha_'.$hashCaptcha;
204204
} else {
205205
// Captcha stuff
206-
$nameField = 'captcha_'.sha1(erLhcoreClassIPDetect::getIP().$_POST['tscaptcha'].erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
206+
$nameField = 'captcha_' . hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $_POST['tscaptcha'].erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
207207
}
208208

209209
$definition[$nameField] = new ezcInputFormDefinitionElement( ezcInputFormDefinitionElement::OPTIONAL, 'string' );
@@ -231,7 +231,7 @@
231231
$item_new->identifier = $identifier;
232232

233233
if (erLhcoreClassModelChatConfig::fetch('session_captcha')->current_value == 1) {
234-
if ( !$form->hasValidData( $nameField ) || $form->$nameField == '' || $form->$nameField < time()-600 || $hashCaptcha != sha1($_SERVER['REMOTE_ADDR'].$form->$nameField.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))){
234+
if ( !$form->hasValidData( $nameField ) || $form->$nameField == '' || $form->$nameField < time()-600 || $hashCaptcha != hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $form->$nameField.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))){
235235
$Errors[] = erTranslationClassLhTranslation::getInstance()->getTranslation("chat/startchat","Your request was not processed as expected - but don't worry it was not your fault. Please re-submit your request. If you experience the same issue you will need to contact us via other means.");
236236
}
237237
} else {

lhc_web/modules/lhquestionary/votingwidget.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@
7272
if (erLhcoreClassModelChatConfig::fetch('session_captcha')->current_value == 1) {
7373
// Start session if required only
7474
$currentUser = erLhcoreClassUser::instance();
75-
$hashCaptcha = isset($_SESSION[$_SERVER['REMOTE_ADDR']]['form']) ? $_SESSION[$_SERVER['REMOTE_ADDR']]['form'] : null;
76-
$nameField = 'captcha_'.$hashCaptcha;
75+
$hashCaptcha = isset($_SESSION[erLhcoreClassIPDetect::getIP()]['form']) ? $_SESSION[erLhcoreClassIPDetect::getIP()]['form'] : null;
76+
$nameField = 'captcha_' . $hashCaptcha;
7777
} else {
78-
$nameField = 'captcha_'.sha1(erLhcoreClassIPDetect::getIP().$_POST['tscaptcha'].erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
78+
$nameField = 'captcha_' . hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $_POST['tscaptcha'].erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ));
7979
}
8080
$definition[$nameField] = new ezcInputFormDefinitionElement( ezcInputFormDefinitionElement::OPTIONAL, 'string' );
8181

@@ -97,7 +97,7 @@
9797
}
9898

9999
if (erLhcoreClassModelChatConfig::fetch('session_captcha')->current_value == 1) {
100-
if ( !$form->hasValidData( $nameField ) || $form->$nameField == '' || $form->$nameField < time()-600 || $hashCaptcha != sha1($_SERVER['REMOTE_ADDR'].$form->$nameField.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))){
100+
if ( !$form->hasValidData( $nameField ) || $form->$nameField == '' || $form->$nameField < time()-600 || $hashCaptcha != hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $form->$nameField.erConfigClassLhConfig::getInstance()->getSetting( 'site', 'secrethash' ))){
101101
$Errors[] = erTranslationClassLhTranslation::getInstance()->getTranslation("chat/startchat","Your request was not processed as expected - but don't worry it was not your fault. Please re-submit your request. If you experience the same issue you will need to contact us via other means.");
102102
}
103103
} else {

lhc_web/modules/lhwidgetrestapi/settings.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@
585585

586586
$cfg = erConfigClassLhConfig::getInstance();
587587

588-
$outputResponse['hash'] = sha1(erLhcoreClassIPDetect::getIP() . $ts . $cfg->getSetting( 'site', 'secrethash' ));
588+
$outputResponse['hash'] = hash('sha256',erLhcoreClassIPDetect::getFingerprint() . $ts . $cfg->getSetting( 'site', 'secrethash' ));
589589
$outputResponse['hash_ts'] = $ts;
590590

591591
if (is_array($department) && !empty($department)) {

0 commit comments

Comments
 (0)