diff --git a/post.php b/post.php index b2ffb7c5..df5e6a72 100644 --- a/post.php +++ b/post.php @@ -92,6 +92,43 @@ function check_recaptcha($secret, $response, $remote_ip) return !!$resp['success']; } +function check_turnstile($secret, $response, $remote_ip, $expected_action) +{ + $data = [ + 'secret' => $secret, + 'response' => $response, + ]; + + if ($remote_ip !== null) { + $data['remoteip'] = $remote_ip; + } + + $c = curl_init(); + curl_setopt_array($c, [ + CURLOPT_URL => 'https://challenges.cloudflare.com/turnstile/v0/siteverify', + CURLOPT_CUSTOMREQUEST => "POST", + CURLOPT_POSTFIELDS => $data, + CURLOPT_RETURNTRANSFER => true, + ]); + $c_ret = curl_exec($c); + if ($c_ret === false) { + $err_no = curl_errno($c); + $err_str = curl_error($c); + + curl_close($c); + error_log("Turnstide call failed. Curl returned: $err_no ($err_str)"); + return false; + } + curl_close($c); + + $json_ret = json_decode($c_ret, true); + if ($json_ret === null) { + error_log("Turnstide call failed. Malformed json: $c_ret"); + return false; + } + return $json_ret['success'] === true && $json_ret['action'] === $expected_action; +} + /** * Deletes the (single) captcha associated with the ip and code. * @@ -690,22 +727,42 @@ function handle_post() if (!$dropped_post) { - if ($config['dynamic_captcha'] && $_SERVER['REMOTE_ADDR'] === '127.0.0.1' && $config['recaptcha']) { - if (!isset($_POST['g-recaptcha-response'])) { - error($config['error']['bot']); - } - if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], null)) { - error($config['error']['captcha']); + if ($config['dynamic_captcha'] && $_SERVER['REMOTE_ADDR'] === '127.0.0.1') { + if ($config['recaptcha']) { + if (!isset($_POST['g-recaptcha-response'])) { + error($config['error']['bot']); + } + if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], null)) { + error($config['error']['captcha']); + } + } elseif ($config['turnstile']) { + if (!isset($_POST['cf-turnstile-response'])) { + error($config['error']['bot']); + } + $expected_action = $post['op'] ? 'post-thread' : 'post-reply'; + if (!check_turnstile($config['turnstile_private'], $_POST['cf-turnstile-response'], null, $expected_action)) { + error($config['error']['captcha']); + } } } // Check for CAPTCHA right after opening the board so the "return" link is in there. - if (!$config['dynamic_captcha'] && $config['recaptcha']) { - if (!isset($_POST['g-recaptcha-response'])) { - error($config['error']['bot']); - } - if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'])) { - error($config['error']['captcha']); + if (!$config['dynamic_captcha']) { + if ($config['recaptcha']) { + if (!isset($_POST['g-recaptcha-response'])) { + error($config['error']['bot']); + } + if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'])) { + error($config['error']['captcha']); + } + } elseif ($config['turnstile']) { + if (!isset($_POST['cf-turnstile-response'])) { + error($config['error']['bot']); + } + $expected_action = $post['op'] ? 'post-thread' : 'post-reply'; + if (!check_turnstile($config['turnstile_private'], $_POST['cf-turnstile-response'], null, $expected_action)) { + error($config['error']['captcha']); + } } }