post.php: workaround captcha and js/ajax.js bug

This commit is contained in:
Zankaria 2024-10-28 12:34:05 +01:00
parent ebc0c54657
commit a7e349d8cb

112
post.php
View file

@ -167,6 +167,68 @@ function check_turnstile($secret, $response, $remote_ip, $expected_action)
return $json_ret['success'] === true && $json_ret['action'] === $expected_action;
}
/**
* A "sophisticated" workaround to js/ajax.js calling post.php multiple times on error/ban.
*/
function check_captcha(array $captcha_config, string $form_id, string $board_uri, string $response, string $remote_ip, string $expected_action) {
$dynamic = $captcha_config['dynamic'];
if ($dynamic !== false && $remote_ip !== $dynamic) {
return true;
}
switch ($captcha_config['mode']) {
case 'recaptcha':
case 'hcaptcha':
case 'turnstile':
$mode = $captcha_config['mode'];
break;
case false:
return true;
default:
\error_log("Unknown captcha mode '{$captcha_config['mode']}'");
throw new \RuntimeException('Captcha configuration error');
}
$passthrough_timeout = $captcha_config['passthrough_timeout'];
if ($passthrough_timeout != 0) {
$pass = Cache::get("captcha_passthrough_{$remote_ip}_{$form_id}");
if ($pass !== false) {
$let_through = $pass['expires'] > time() && $pass['board_uri'] === $board_uri && $pass['captcha_response'] === $response;
if ($let_through) {
return true;
}
}
}
$remote_ip_send = $dynamic !== false ? null : $remote_ip;
$private_key = $captcha_config[$mode]['private'];
$public_key = $captcha_config[$mode]['public'];
switch ($mode) {
case 'recaptcha':
$ret = check_recaptcha($private_key, $response, $remote_ip_send);
break;
case 'hcaptcha':
$ret = check_hcaptcha($private_key, $response, $remote_ip_send, $public_key);
break;
case 'turnstile':
$ret = check_turnstile($private_key, $response, $remote_ip_send, $expected_action);
break;
}
if ($ret && $passthrough_timeout != 0) {
$pass = [
'expires' => time() + $passthrough_timeout,
'board_uri' => $board_uri,
'captcha_response' => $response
];
Cache::set("captcha_passthrough_{$remote_ip}_{$form_id}", $pass, $passthrough_timeout + 2);
}
return $ret;
}
/**
* Deletes the (single) captcha associated with the ip and code.
*
@ -765,58 +827,18 @@ function handle_post()
if (!$dropped_post) {
if ($config['dynamic_captcha'] !== false) {
if ($_SERVER['REMOTE_ADDR'] === $config['dynamic_captcha']) {
if ($config['recaptcha']) {
if (!isset($_POST['captcha-response'])) {
error($config['error']['bot']);
}
if (!check_recaptcha($config['recaptcha_private'], $_POST['captcha-response'], null)) {
error($config['error']['captcha']);
}
} elseif ($config['hcaptcha']) {
if (!isset($_POST['captcha-response'])) {
error($config['error']['bot']);
}
if (!check_hcaptcha($config['hcaptcha_private'], $_POST['captcha-response'], null, $config['hcaptcha_public'])) {
error($config['error']['captcha']);
}
} elseif ($config['turnstile']) {
if (!isset($_POST['captcha-response'])) {
error($config['error']['bot']);
}
$expected_action = $post['op'] ? 'post-thread' : 'post-reply';
if (!check_turnstile($config['turnstile_private'], $_POST['captcha-response'], null, $expected_action)) {
error($config['error']['captcha']);
}
}
}
} else {
// Check for CAPTCHA right after opening the board so the "return" link is in there.
if ($config['recaptcha']) {
if (!isset($_POST['captcha-response'])) {
error($config['error']['bot']);
}
if (!check_recaptcha($config['recaptcha_private'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'])) {
error($config['error']['captcha']);
}
} elseif ($config['hcaptcha']) {
if (!isset($_POST['captcha-response'])) {
error($config['error']['bot']);
}
if (!check_hcaptcha($config['hcaptcha_private'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'], $config['hcaptcha_public'])) {
error($config['error']['captcha']);
}
} elseif ($config['turnstile']) {
if (!isset($_POST['captcha-response'])) {
if ($config['captcha']['mode'] !== false) {
if (!isset($_POST['captcha-response'], $_POST['captcha-form-id'])) {
error($config['error']['bot']);
}
$expected_action = $post['op'] ? 'post-thread' : 'post-reply';
if (!check_turnstile($config['turnstile_private'], $_POST['captcha-response'], null, $expected_action)) {
$ret = check_captcha($config['captcha'], $_POST['captcha-form-id'], $post['board'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'], $expected_action);
if (!$ret) {
error($config['error']['captcha']);
}
}
}
if (isset($config['simple_spam']) && $post['op']) {
if (!isset($_POST['simple_spam']) || $config['simple_spam']['answer'] != $_POST['simple_spam']) {