diff --git a/config/config.ini.example b/config/config.ini.example
index d55b50a..f657b1f 100644
--- a/config/config.ini.example
+++ b/config/config.ini.example
@@ -59,12 +59,13 @@ google.analytics.id = ""
; Google gtag analytics
google.gtag.id = ""
-; Google reCaptcha
-; https://www.google.com/recaptcha/admin. Options "false" and "true"
+; Login protection system Choose "google", "cloudflare", or "disable".
+; https://www.google.com/recaptcha/admin
+; https://developers.cloudflare.com/turnstile/
-google.reCaptcha = "false"
-google.reCaptcha.public = ""
-google.reCaptcha.private = ""
+login.protect.system = "disable"
+login.protect.public = ""
+login.protect.private = ""
; Pagination, RSS, and JSON
posts.perpage = "10"
diff --git a/lang/en_US.ini b/lang/en_US.ini
index aaa42ad..2b91046 100644
--- a/lang/en_US.ini
+++ b/lang/en_US.ini
@@ -37,7 +37,7 @@ cache_off = "Cache off"
cache_timestamp = "Cache timestamp"
cancel = "Cancel"
cannot_read_feed_content = "Cannot read feed content"
-captcha_error = "reCaptcha not correct"
+captcha_error = "Captcha failed"
categories = "Categories"
category = "Category"
check_update = "Check for update"
@@ -87,7 +87,7 @@ front_page_displays = "Front page displays"
full_post = "Full post"
general = "General"
general_settings = "General Settings"
-get_one_here = "Get one here"
+get_one_here = "Obtain your reCaptcha keys here: "
github_pre_release = "Github pre-release"
google_analytics = "Google Analytics"
google_analytics_legacy = "Google Analytics (legacy)"
@@ -186,7 +186,7 @@ reading = "Reading"
writing = "Writing"
reading_settings = "Reading Settings"
writing_settings = "Writing Settings"
-recaptcha = "reCAPTCHA"
+recaptcha = "Login Protection"
recent_posts = "Recent posts"
recent_posts_widget_at_most = "Recent posts widget at most"
regular_post = "Regular post"
@@ -297,3 +297,5 @@ mfa_error = "MFA code is not correct"
disablemfa = "Disable MFA"
enable_auto_save = "Enable Auto Save to Drafts"
explain_autosave = "When enabled, new posts or pages will automatically be saved as a draft every 60 seconds after you start writing."
+login_protect_system = "Login protection system"
+cloudflare_info = "Review Cloudflare's Turnstile documentation: "
\ No newline at end of file
diff --git a/system/admin/views/config-widget.html.php b/system/admin/views/config-widget.html.php
index c83f9fc..f1bf7bd 100644
--- a/system/admin/views/config-widget.html.php
+++ b/system/admin/views/config-widget.html.php
@@ -176,35 +176,42 @@
https://www.google.com/recaptcha/admin
+
https://developers.cloudflare.com/turnstile/
diff --git a/system/admin/views/login.html.php b/system/admin/views/login.html.php
index 8ab940d..3501edc 100644
--- a/system/admin/views/login.html.php
+++ b/system/admin/views/login.html.php
@@ -24,9 +24,14 @@
-
+
- ">
+ ">
+
+
+
+
+ ">
diff --git a/system/configList.json b/system/configList.json
index 84f89b1..b5ea594 100644
--- a/system/configList.json
+++ b/system/configList.json
@@ -27,9 +27,9 @@
"google.wmt.id",
"google.analytics.id",
"google.gtag.id",
- "google.reCaptcha",
- "google.reCaptcha.public",
- "google.reCaptcha.private",
+ "login.protect.system",
+ "login.protect.public",
+ "login.protect.private",
"posts.perpage",
"category.perpage",
"tag.perpage",
diff --git a/system/htmly.php b/system/htmly.php
index b4d72f8..6400388 100644
--- a/system/htmly.php
+++ b/system/htmly.php
@@ -121,7 +121,13 @@ get('/index', function () {
post('/login', function () {
$proper = (is_csrf_proper(from($_REQUEST, 'csrf_token')));
+ if (config('login.protect.system') === 'google') {
$captcha = isCaptcha(from($_REQUEST, 'g-recaptcha-response'));
+ } elseif (config('login.protect.system') === 'cloudflare') {
+ $captcha = isTurnstile(from($_REQUEST, 'cf-turnstile-response'));
+ } else {
+ $captcha = true;
+ }
$user = from($_REQUEST, 'user');
$pass = from($_REQUEST, 'password');
diff --git a/system/includes/functions.php b/system/includes/functions.php
index aa76d08..5ab35c6 100644
--- a/system/includes/functions.php
+++ b/system/includes/functions.php
@@ -3561,12 +3561,9 @@ function remove_html_comments($content)
// Google recaptcha
function isCaptcha($reCaptchaResponse)
{
- if (config('google.reCaptcha') != 'true') {
- return true;
- }
$url = "https://www.google.com/recaptcha/api/siteverify";
$options = array(
- "secret" => config("google.reCaptcha.private"),
+ "secret" => config("login.protect.private"),
"response" => $reCaptchaResponse,
"remoteip" => $_SERVER['REMOTE_ADDR'],
);
@@ -3581,6 +3578,35 @@ function isCaptcha($reCaptchaResponse)
return ($json['success']);
}
+// Cloudflare Turnstile
+function isTurnstile($turnstileResponse)
+{
+ $public = config("login.protect.public");
+ $private = config("login.protect.private");
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
+ $data = array('secret' => $private, 'response' => $turnstileResponse, 'remoteip' => $ip);
+
+ $options = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'content' => http_build_query($data))
+ );
+
+ $stream = stream_context_create($options);
+ $fileContent = file_get_contents($url, false, $stream);
+
+ if ($fileContent === false) {
+ return false;
+ }
+ $json = json_decode($fileContent, true);
+ if ($json == false) {
+ return false;
+ }
+ return ($json['success']);
+}
+
// Get video ID
function get_video_id($url)
{