From e825e7aac52f52c81b32db4a39cc8304d6a5d1a3 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Sat, 25 May 2024 00:32:44 +0200 Subject: [PATCH 1/7] Add dynamic captcha support --- inc/config.php | 9 +++++++++ post.php | 2 +- templates/main.js | 30 ++++++++++++++++++++++++++++++ templates/post_form.html | 4 ++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index 319bb8fd..79181790 100644 --- a/inc/config.php +++ b/inc/config.php @@ -351,6 +351,15 @@ //); $config['simple_spam'] = false; + /* + * If not flase, the captcha is dynamically injected on the client if the web server set the `captcha-required` + * cookie to 1. The configuration value should be set the IP for which the captcha should be verified. + * + * Example: + * $config['dynamic_captcha'] = '127.0.0.1'; // Verify the captcha for users sending posts from the loopback address. + */ + $config['dynamic_captcha'] = false; + // Enable reCaptcha to make spam even harder. Rarely necessary. $config['recaptcha'] = false; diff --git a/post.php b/post.php index 644ff111..a7f7e88b 100644 --- a/post.php +++ b/post.php @@ -648,7 +648,7 @@ if (isset($_POST['delete'])) { } } // Remote 3rd party captchas. - else { + else if (!$config['dynamic_captcha'] || $config['dynamic_captcha'] === $_SERVER['REMOTE_ADDR']) { // recaptcha if ($config['recaptcha']) { if (!isset($_POST['g-recaptcha-response'])) { diff --git a/templates/main.js b/templates/main.js index a850787f..e0819622 100644 --- a/templates/main.js +++ b/templates/main.js @@ -222,6 +222,36 @@ function getCookie(cookie_name) { } } +{% endraw %} +{% if config.dynamic_captcha %} +function is_dynamic_captcha_enabled() { + let cookie = get_cookie('require-captcha'); + return cookie === '1'; +} + +function get_captcha_pub_key() { +{% if config.recaptcha %} + return "{{ config.recaptcha_public }}"; +{% else %} + return null; +{% endif %} +} + +function init_dynamic_captcha() { + if (!is_dynamic_captcha_enabled()) { + let pub_key = get_captcha_pub_key(); + if (!pub_key) { + console.error("Missing public captcha key!"); + return; + } + + let captcha_hook = document.getElementById('captcha'); + captcha_hook.style = ""; + } +} +{% endif %} +{% raw %} + function highlightReply(id) { if (typeof window.event != "undefined" && event.which == 2) { // don't highlight on middle click diff --git a/templates/post_form.html b/templates/post_form.html index 5a6854cf..8220107b 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -73,7 +73,11 @@ {% if config.recaptcha %} + {% if config.dynamic_captcha %} + + {% else %} + {% endif %} {% trans %}Verification{% endtrans %} {{ antibot.html() }} From 933594194c477839dfbf02422bbcba62be807709 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 15:11:32 +0200 Subject: [PATCH 2/7] captcha-queries.php: refactor remote captcha queries to use DI and abstract the implementation better --- inc/context.php | 14 ++++ inc/service/captcha-queries.php | 119 +++++++++++++++++++++----------- post.php | 34 ++++----- 3 files changed, 105 insertions(+), 62 deletions(-) diff --git a/inc/context.php b/inc/context.php index f65c3b0d..e6604835 100644 --- a/inc/context.php +++ b/inc/context.php @@ -3,6 +3,9 @@ namespace Vichan; use RuntimeException; use Vichan\Driver\{HttpDriver, HttpDrivers, Log, LogDrivers}; +use Vichan\Service\HCaptchaQuery; +use Vichan\Service\ReCaptchaQuery; +use Vichan\Service\RemoteCaptchaQuery; defined('TINYBOARD') or exit; @@ -53,6 +56,17 @@ function build_context(array $config): Context { HttpDriver::class => function($c) { $config = $c->get('config'); return HttpDrivers::getHttpDriver($config['upload_by_url_timeout'], $config['max_filesize']); + }, + RemoteCaptchaQuery::class => function($c) { + $config = $c->get('config'); + $http = $c->get(HttpDriver::class); + if ($config['recaptcha']) { + return new ReCaptchaQuery($http, $config['recaptcha_private']); + } elseif ($config['hcaptcha']) { + return new HCaptchaQuery($http, $config['hcaptcha_private'], $config['hcaptcha_public']); + } else { + throw new RuntimeException('No remote captcha service available'); + } } ]); } diff --git a/inc/service/captcha-queries.php b/inc/service/captcha-queries.php index d7966501..cbd344a1 100644 --- a/inc/service/captcha-queries.php +++ b/inc/service/captcha-queries.php @@ -6,68 +6,107 @@ use Vichan\Driver\HttpDriver; defined('TINYBOARD') or exit; -class RemoteCaptchaQuery { +class ReCaptchaQuery implements RemoteCaptchaQuery { private HttpDriver $http; private string $secret; - private string $endpoint; - /** - * Creates a new CaptchaRemoteQueries instance using the google recaptcha service. + * Creates a new ReCaptchaQuery using the google recaptcha service. * * @param HttpDriver $http The http client. * @param string $secret Server side secret. - * @return CaptchaRemoteQueries A new captcha query instance. + * @return ReCaptchaQuery A new ReCaptchaQuery query instance. */ - public static function withRecaptcha(HttpDriver $http, string $secret): RemoteCaptchaQuery { - return new self($http, $secret, 'https://www.google.com/recaptcha/api/siteverify'); - } - - /** - * Creates a new CaptchaRemoteQueries instance using the hcaptcha service. - * - * @param HttpDriver $http The http client. - * @param string $secret Server side secret. - * @return CaptchaRemoteQueries A new captcha query instance. - */ - public static function withHCaptcha(HttpDriver $http, string $secret): RemoteCaptchaQuery { - return new self($http, $secret, 'https://hcaptcha.com/siteverify'); - } - - private function __construct(HttpDriver $http, string $secret, string $endpoint) { + public function __construct(HttpDriver $http, string $secret) { $this->http = $http; $this->secret = $secret; - $this->endpoint = $endpoint; } - /** - * Checks if the user at the remote ip passed the captcha. - * - * @param string $response User provided response. - * @param string $remote_ip User ip. - * @return bool Returns true if the user passed the captcha. - * @throws RuntimeException|JsonException Throws on IO errors or if it fails to decode the answer. - */ - public function verify(string $response, string $remote_ip): bool { - $data = array( - 'secret' => $this->secret, - 'response' => $response, - 'remoteip' => $remote_ip - ); + public function responseField(): string { + return 'g-recaptcha-response'; + } - $ret = $this->http->requestGet($this->endpoint, $data); + public function verify(string $response, ?string $remote_ip): bool { + $data = [ + 'secret' => $this->secret, + 'response' => $response + ]; + + if ($remote_ip !== null) { + $data['remoteip'] = $remote_ip; + } + + $ret = $this->http->requestGet('https://www.google.com/recaptcha/api/siteverify', $data); $resp = json_decode($ret, true, 16, JSON_THROW_ON_ERROR); return isset($resp['success']) && $resp['success']; } } +class HCaptchaQuery implements RemoteCaptchaQuery { + private HttpDriver $http; + private string $secret; + private string $sitekey; + + /** + * Creates a new HCaptchaQuery using the hCaptcha service. + * + * @param HttpDriver $http The http client. + * @param string $secret Server side secret. + * @return HCaptchaQuery A new hCaptcha query instance. + */ + public function __construct(HttpDriver $http, string $secret, string $sitekey) { + $this->http = $http; + $this->secret = $secret; + $this->sitekey = $sitekey; + } + + public function responseField(): string { + return 'h-captcha-response'; + } + + public function verify(string $response, ?string $remote_ip): bool { + $data = [ + 'secret' => $this->secret, + 'response' => $response, + 'sitekey' => $this->sitekey + ]; + + if ($remote_ip !== null) { + $data['remoteip'] = $remote_ip; + } + + $ret = $this->http->requestGet('https://hcaptcha.com/siteverify', $data); + $resp = json_decode($ret, true, 16, JSON_THROW_ON_ERROR); + + return isset($resp['success']) && $resp['success']; + } +} + +interface RemoteCaptchaQuery { + /** + * Name of the response field in the form data expected by the implementation. + * + * @return string The name of the field. + */ + public function responseField(): string; + + /** + * Checks if the user at the remote ip passed the captcha. + * + * @param string $response User provided response. + * @param ?string $remote_ip User ip. Leave to null to only check the response value. + * @return bool Returns true if the user passed the captcha. + * @throws RuntimeException|JsonException Throws on IO errors or if it fails to decode the answer. + */ + public function verify(string $response, ?string $remote_ip): bool; +} + class NativeCaptchaQuery { private HttpDriver $http; private string $domain; private string $provider_check; - /** * @param HttpDriver $http The http client. * @param string $domain The server's domain. @@ -89,12 +128,12 @@ class NativeCaptchaQuery { * @throws RuntimeException Throws on IO errors. */ public function verify(string $extra, string $user_text, string $user_cookie): bool { - $data = array( + $data = [ 'mode' => 'check', 'text' => $user_text, 'extra' => $extra, 'cookie' => $user_cookie - ); + ]; $ret = $this->http->requestGet($this->domain . '/' . $this->provider_check, $data); return $ret === '1'; diff --git a/post.php b/post.php index a7f7e88b..fd78d749 100644 --- a/post.php +++ b/post.php @@ -648,29 +648,19 @@ if (isset($_POST['delete'])) { } } // Remote 3rd party captchas. - else if (!$config['dynamic_captcha'] || $config['dynamic_captcha'] === $_SERVER['REMOTE_ADDR']) { - // recaptcha - if ($config['recaptcha']) { - if (!isset($_POST['g-recaptcha-response'])) { - error($config['error']['bot']); - } - $response = $_POST['g-recaptcha-response']; - $query = RemoteCaptchaQuery::withRecaptcha($context->get(HttpDriver::class), $config['recaptcha_private']); - } - // hCaptcha - elseif ($config['hcaptcha']) { - if (!isset($_POST['h-captcha-response'])) { - error($config['error']['bot']); - } - $response = $_POST['h-captcha-response']; - $query = RemoteCaptchaQuery::withHCaptcha($context->get(HttpDriver::class), $config['hcaptcha_private']); - } + elseif (($config['recaptcha'] || $config['hcaptcha']) + && (!$config['dynamic_captcha'] || $config['dynamic_captcha'] === $_SERVER['REMOTE_ADDR'])) { + $query = $content->get(RemoteCaptchaQuery::class); + $field = $query->responseField(); - if (isset($query, $response)) { - $success = $query->verify($response, $_SERVER['REMOTE_ADDR']); - if (!$success) { - error($config['error']['captcha']); - } + if (!isset($_POST[$field])) { + error($config['error']['bot']); + } + $response = $_POST[$field]; + + $success = $query->verify($response, $_SERVER['REMOTE_ADDR']); + if (!$success) { + error($config['error']['captcha']); } } } catch (RuntimeException $e) { From a275d04efa6cb1f4160d3a6d0bd5ee5e494f0f92 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 15:17:54 +0200 Subject: [PATCH 3/7] captcha-queries.php: refactor NativeCaptchaQuery to use DI --- inc/context.php | 10 ++++++++++ inc/service/captcha-queries.php | 10 ++++++---- post.php | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/inc/context.php b/inc/context.php index e6604835..30c9804e 100644 --- a/inc/context.php +++ b/inc/context.php @@ -4,6 +4,7 @@ namespace Vichan; use RuntimeException; use Vichan\Driver\{HttpDriver, HttpDrivers, Log, LogDrivers}; use Vichan\Service\HCaptchaQuery; +use Vichan\Service\NativeCaptchaQuery; use Vichan\Service\ReCaptchaQuery; use Vichan\Service\RemoteCaptchaQuery; @@ -67,6 +68,15 @@ function build_context(array $config): Context { } else { throw new RuntimeException('No remote captcha service available'); } + }, + NativeCaptchaQuery::class => function($c) { + $http = $c->get(HttpDriver::class); + $config = $c->get('config'); + return new NativeCaptchaQuery($http, + $config['domain'], + $config['captcha']['provider_check'], + $config['captcha']['extra'] + ); } ]); } diff --git a/inc/service/captcha-queries.php b/inc/service/captcha-queries.php index cbd344a1..cd9a1b84 100644 --- a/inc/service/captcha-queries.php +++ b/inc/service/captcha-queries.php @@ -106,32 +106,34 @@ class NativeCaptchaQuery { private HttpDriver $http; private string $domain; private string $provider_check; + private string $extra; /** * @param HttpDriver $http The http client. * @param string $domain The server's domain. * @param string $provider_check Path to the endpoint. + * @param string $extra Extra http parameters. */ - function __construct(HttpDriver $http, string $domain, string $provider_check) { + function __construct(HttpDriver $http, string $domain, string $provider_check, string $extra) { $this->http = $http; $this->domain = $domain; $this->provider_check = $provider_check; + $this->extra = $extra; } /** * Checks if the user at the remote ip passed the native vichan captcha. * - * @param string $extra Extra http parameters. * @param string $user_text Remote user's text input. * @param string $user_cookie Remote user cookie. * @return bool Returns true if the user passed the check. * @throws RuntimeException Throws on IO errors. */ - public function verify(string $extra, string $user_text, string $user_cookie): bool { + public function verify(string $user_text, string $user_cookie): bool { $data = [ 'mode' => 'check', 'text' => $user_text, - 'extra' => $extra, + 'extra' => $this->extra, 'cookie' => $user_cookie ]; diff --git a/post.php b/post.php index fd78d749..ea5b1ccf 100644 --- a/post.php +++ b/post.php @@ -631,8 +631,8 @@ if (isset($_POST['delete'])) { try { // With our custom captcha provider if ($config['captcha']['enabled'] || ($post['op'] && $config['new_thread_capt'])) { - $query = new NativeCaptchaQuery($context->get(HttpDriver::class), $config['domain'], $config['captcha']['provider_check']); - $success = $query->verify($config['captcha']['extra'], $_POST['captcha_text'], $_POST['captcha_cookie']); + $query = $context->get(NativeCaptchaQuery::class); + $success = $query->verify($_POST['captcha_text'], $_POST['captcha_cookie']); if (!$success) { error( From fb92e5fb682df82a265030ae93bf0707f1e2d966 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 16:11:28 +0200 Subject: [PATCH 4/7] config.php: rework captcha configuration --- inc/config.php | 75 ++++++++++++++++++---------------------- inc/context.php | 28 +++++++++------ post.php | 10 ++++-- templates/header.html | 4 +-- templates/main.js | 6 ++-- templates/post_form.html | 18 +++++----- 6 files changed, 72 insertions(+), 69 deletions(-) diff --git a/inc/config.php b/inc/config.php index 79181790..89ea333a 100644 --- a/inc/config.php +++ b/inc/config.php @@ -351,48 +351,39 @@ //); $config['simple_spam'] = false; - /* - * If not flase, the captcha is dynamically injected on the client if the web server set the `captcha-required` - * cookie to 1. The configuration value should be set the IP for which the captcha should be verified. - * - * Example: - * $config['dynamic_captcha'] = '127.0.0.1'; // Verify the captcha for users sending posts from the loopback address. - */ - $config['dynamic_captcha'] = false; - - // Enable reCaptcha to make spam even harder. Rarely necessary. - $config['recaptcha'] = false; - - // Public and private key pair from https://www.google.com/recaptcha/admin/create - $config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f'; - $config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_'; - - // Enable hCaptcha as an alternative to reCAPTCHA. - $config['hcaptcha'] = false; - - // Public and private key pair for using hCaptcha. - $config['hcaptcha_public'] = '7a4b21e0-dc53-46f2-a9f8-91d2e74b63a0'; - $config['hcaptcha_private'] = '0x4e9A01bE637b51dC41a7Ea9865C3fDe4aB72Cf17'; - - // Enable Custom Captcha you need to change a couple of settings - //Read more at: /inc/captcha/readme.md - $config['captcha'] = array(); - - // Enable custom captcha provider - $config['captcha']['enabled'] = false; - - //New thread captcha - //Require solving a captcha to post a thread. - //Default off. - $config['new_thread_capt'] = false; - - // Custom captcha get provider path (if not working get the absolute path aka your url.) - $config['captcha']['provider_get'] = '../inc/captcha/entrypoint.php'; - // Custom captcha check provider path - $config['captcha']['provider_check'] = '../inc/captcha/entrypoint.php'; - - // Custom captcha extra field (eg. charset) - $config['captcha']['extra'] = 'abcdefghijklmnopqrstuvwxyz'; + $config['captcha'] = [ + // Can be false, 'recaptcha', 'hcaptcha' or 'secureimage'. + 'provider' => false, + /* + * If not false, the captcha is dynamically injected on the client if the web server set the `captcha-required` + * cookie to 1. The configuration value should be set the IP for which the captcha should be verified. + * + * Example: + * + * // Verify the captcha for users sending posts from the loopback address. + * $config['captcha']['dynamic'] = '127.0.0.1'; + */ + 'dynamic' => false, + 'recaptcha' => [ + 'sitekey' => '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI', + 'secret' => '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe', + ], + 'hcaptcha' => [ + 'sitekey' => '10000000-ffff-ffff-ffff-000000000001', + 'secret' => '0x0000000000000000000000000000000000000000', + ], + // Enable the secureimage captcha you need to change a couple of settings. Read more at: /inc/captcha/readme.md + 'secureimage' => [ + // Custom captcha get provider path (if not working get the absolute path aka your url). + 'provider_get' => '../inc/captcha/entrypoint.php', + // Custom captcha check provider path + 'provider_check' => '../inc/captcha/entrypoint.php', + // Custom captcha extra field (eg. charset) + 'extra' => 'abcdefghijklmnopqrstuvwxyz', + // New thread captcha. Require solving a captcha to post a thread. + 'new_thread_capt' => false + ] + ]; // Ability to lock a board for normal users and still allow mods to post. Could also be useful for making an archive board $config['board_locked'] = false; diff --git a/inc/context.php b/inc/context.php index 30c9804e..23165377 100644 --- a/inc/context.php +++ b/inc/context.php @@ -61,21 +61,29 @@ function build_context(array $config): Context { RemoteCaptchaQuery::class => function($c) { $config = $c->get('config'); $http = $c->get(HttpDriver::class); - if ($config['recaptcha']) { - return new ReCaptchaQuery($http, $config['recaptcha_private']); - } elseif ($config['hcaptcha']) { - return new HCaptchaQuery($http, $config['hcaptcha_private'], $config['hcaptcha_public']); - } else { - throw new RuntimeException('No remote captcha service available'); + switch ($config['captcha']['provider']) { + case 'recaptcha': + return new ReCaptchaQuery($http, $config['captcha']['recaptcha']['secret']); + case 'hcaptcha': + return new HCaptchaQuery( + $http, + $config['captcha']['hcaptcha']['secret'], + $config['captcha']['hcaptcha']['sitekey'] + ); + default: + throw new RuntimeException('No remote captcha service available'); } }, NativeCaptchaQuery::class => function($c) { - $http = $c->get(HttpDriver::class); $config = $c->get('config'); - return new NativeCaptchaQuery($http, + if ($config['captcha']['provider'] !== 'secureimage') { + throw new RuntimeException('No native captcha service available'); + } + return new NativeCaptchaQuery( + $c->get(HttpDriver::class), $config['domain'], - $config['captcha']['provider_check'], - $config['captcha']['extra'] + $config['captcha']['secureimage']['provider_check'], + $config['captcha']['secureimage']['extra'] ); } ]); diff --git a/post.php b/post.php index ea5b1ccf..f937f06c 100644 --- a/post.php +++ b/post.php @@ -629,8 +629,13 @@ if (isset($_POST['delete'])) { // Check for CAPTCHA right after opening the board so the "return" link is in there. try { + $provider = $config['captcha']['provider']; + $new_thread_capt = $config['captcha']['secureimage']['new_thread_capt']; + $dynamic = $config['captcha']['dynamic']; + // With our custom captcha provider - if ($config['captcha']['enabled'] || ($post['op'] && $config['new_thread_capt'])) { + if (($provider === 'secureimage' && !$new_thread_capt) + || ($provider === 'secureimage' && $new_thread_capt && $post['op'])) { $query = $context->get(NativeCaptchaQuery::class); $success = $query->verify($_POST['captcha_text'], $_POST['captcha_cookie']); @@ -648,8 +653,7 @@ if (isset($_POST['delete'])) { } } // Remote 3rd party captchas. - elseif (($config['recaptcha'] || $config['hcaptcha']) - && (!$config['dynamic_captcha'] || $config['dynamic_captcha'] === $_SERVER['REMOTE_ADDR'])) { + elseif ($provider && (!$dynamic || $dynamic === $_SERVER['REMOTE_ADDR'])) { $query = $content->get(RemoteCaptchaQuery::class); $field = $query->responseField(); diff --git a/templates/header.html b/templates/header.html index d35fabb9..f72b752a 100644 --- a/templates/header.html +++ b/templates/header.html @@ -20,7 +20,7 @@ {% endif %} {% endif %} - {% if config.recaptcha %} + {% if config.captcha.provider == 'recaptcha' %} {% endif %} - {% if config.hcaptcha %} + {% if config.captcha.provider.hcaptcha %} {% endif %} diff --git a/templates/main.js b/templates/main.js index e0819622..f956f5cf 100644 --- a/templates/main.js +++ b/templates/main.js @@ -223,15 +223,15 @@ function getCookie(cookie_name) { } {% endraw %} -{% if config.dynamic_captcha %} +{% if config.captcha.dynamic %} function is_dynamic_captcha_enabled() { let cookie = get_cookie('require-captcha'); return cookie === '1'; } function get_captcha_pub_key() { -{% if config.recaptcha %} - return "{{ config.recaptcha_public }}"; +{% if config.captcha.provider === 'recaptcha' %} + return "{{ config.captcha.recaptcha.sitekey }}"; {% else %} return null; {% endif %} diff --git a/templates/post_form.html b/templates/post_form.html index 8220107b..19c69cfb 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -72,8 +72,8 @@ {% endif %} - {% if config.recaptcha %} - {% if config.dynamic_captcha %} + {% if config.captcha.provider == 'recaptcha' %} + {% if config.captcha.dynamic %} {% else %} @@ -83,19 +83,19 @@ {{ antibot.html() }} -
+
{{ antibot.html() }} {% endif %} - {% if config.hcaptcha %} + {% if config.captcha.provider == 'hcaptcha' %} {% trans %}Verification{% endtrans %} {{ antibot.html() }} -
+
{{ antibot.html() }} @@ -106,11 +106,11 @@ {% trans %}Verification{% endtrans %} - + @@ -122,11 +122,11 @@ {% trans %}Verification{% endtrans %} - + From cb5fb68c5e2c9d28452de72b0817b56d6e7782a2 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 16:12:32 +0200 Subject: [PATCH 5/7] header.html: format --- templates/header.html | 110 +++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/templates/header.html b/templates/header.html index f72b752a..14a42952 100644 --- a/templates/header.html +++ b/templates/header.html @@ -1,55 +1,55 @@ - - {% if config.url_favicon %}{% endif %} - - - {% if config.meta_keywords %}{% endif %} - {% if config.default_stylesheet.1 != '' %}{% endif %} - {% if config.font_awesome %}{% endif %} - {% if config.country_flags_condensed %}{% endif %} - - {% if not nojavascript %} - - {% if not config.additional_javascript_compile %} - {% for javascript in config.additional_javascript %}{% endfor %} - {% endif %} - {% if mod %} - - {% endif %} - {% endif %} - {% if config.captcha.provider == 'recaptcha' %} - {% endif %} - {% if config.captcha.provider.hcaptcha %} - - {% endif %} + +{% if config.url_favicon %}{% endif %} + + +{% if config.meta_keywords %}{% endif %} +{% if config.default_stylesheet.1 != '' %}{% endif %} +{% if config.font_awesome %}{% endif %} +{% if config.country_flags_condensed %}{% endif %} + +{% if not nojavascript %} + + {% if not config.additional_javascript_compile %} + {% for javascript in config.additional_javascript %}{% endfor %} + {% endif %} + {% if mod %} + + {% endif %} +{% endif %} +{% if config.captcha.provider == 'recaptcha' %} +{% endif %} +{% if config.captcha.provider.hcaptcha %} + +{% endif %} From f7073d5d7ef6c923437ad36df0cb1b90b092d91f Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 16:22:58 +0200 Subject: [PATCH 6/7] post.php: do not verify the poster IP if the captcha is dynamic --- post.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/post.php b/post.php index f937f06c..b461c47e 100644 --- a/post.php +++ b/post.php @@ -661,8 +661,13 @@ if (isset($_POST['delete'])) { error($config['error']['bot']); } $response = $_POST[$field]; + /* + * Do not query with the IP if the mode is dynamic. This config is meant for proxies and internal + * loopback addresses. + */ + $ip = $dynamic ? null : $_SERVER['REMOTE_ADDR']; - $success = $query->verify($response, $_SERVER['REMOTE_ADDR']); + $success = $query->verify($response, $ip); if (!$success) { error($config['error']['captcha']); } From 165ea5308ac7400e785e8322999d316aea43ff5a Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 15 Aug 2024 16:33:56 +0200 Subject: [PATCH 7/7] config.php: ops, wrong name for the native captcha --- inc/config.php | 6 +++--- inc/context.php | 6 +++--- post.php | 6 +++--- templates/post_form.html | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/inc/config.php b/inc/config.php index 89ea333a..b4fc27d8 100644 --- a/inc/config.php +++ b/inc/config.php @@ -352,7 +352,7 @@ $config['simple_spam'] = false; $config['captcha'] = [ - // Can be false, 'recaptcha', 'hcaptcha' or 'secureimage'. + // Can be false, 'recaptcha', 'hcaptcha' or 'native'. 'provider' => false, /* * If not false, the captcha is dynamically injected on the client if the web server set the `captcha-required` @@ -372,8 +372,8 @@ 'sitekey' => '10000000-ffff-ffff-ffff-000000000001', 'secret' => '0x0000000000000000000000000000000000000000', ], - // Enable the secureimage captcha you need to change a couple of settings. Read more at: /inc/captcha/readme.md - 'secureimage' => [ + // To enable the native captcha you need to change a couple of settings. Read more at: /inc/captcha/readme.md + 'native' => [ // Custom captcha get provider path (if not working get the absolute path aka your url). 'provider_get' => '../inc/captcha/entrypoint.php', // Custom captcha check provider path diff --git a/inc/context.php b/inc/context.php index 23165377..3f8a75d6 100644 --- a/inc/context.php +++ b/inc/context.php @@ -76,14 +76,14 @@ function build_context(array $config): Context { }, NativeCaptchaQuery::class => function($c) { $config = $c->get('config'); - if ($config['captcha']['provider'] !== 'secureimage') { + if ($config['captcha']['provider'] !== 'native') { throw new RuntimeException('No native captcha service available'); } return new NativeCaptchaQuery( $c->get(HttpDriver::class), $config['domain'], - $config['captcha']['secureimage']['provider_check'], - $config['captcha']['secureimage']['extra'] + $config['captcha']['native']['provider_check'], + $config['captcha']['native']['extra'] ); } ]); diff --git a/post.php b/post.php index b461c47e..eafb1130 100644 --- a/post.php +++ b/post.php @@ -630,12 +630,12 @@ if (isset($_POST['delete'])) { // Check for CAPTCHA right after opening the board so the "return" link is in there. try { $provider = $config['captcha']['provider']; - $new_thread_capt = $config['captcha']['secureimage']['new_thread_capt']; + $new_thread_capt = $config['captcha']['native']['new_thread_capt']; $dynamic = $config['captcha']['dynamic']; // With our custom captcha provider - if (($provider === 'secureimage' && !$new_thread_capt) - || ($provider === 'secureimage' && $new_thread_capt && $post['op'])) { + if (($provider === 'native' && !$new_thread_capt) + || ($provider === 'native' && $new_thread_capt && $post['op'])) { $query = $context->get(NativeCaptchaQuery::class); $success = $query->verify($_POST['captcha_text'], $_POST['captcha_cookie']); diff --git a/templates/post_form.html b/templates/post_form.html index 19c69cfb..038395fa 100644 --- a/templates/post_form.html +++ b/templates/post_form.html @@ -106,11 +106,11 @@ {% trans %}Verification{% endtrans %} - + @@ -122,11 +122,11 @@ {% trans %}Verification{% endtrans %} - +