diff --git a/captcha.lib.php b/captcha.lib.php new file mode 100644 index 0000000..8d43894 --- /dev/null +++ b/captcha.lib.php @@ -0,0 +1,72 @@ +'; +} + +function captcha_widget() { + return '
'; +} + +// Ask Google whether the captcha in this POST was solved +function captcha_verify_response() { + $response = $_POST['g-recaptcha-response'] ?? ''; + if (!is_string($response) || $response === '') + return FALSE; + $secret = (string)config('RECAPTCHA_SECRET'); + if ($secret === '') { + error_log('xxx: RECAPTCHA_SECRET is not set, so every captcha check fails'); + return FALSE; + } + $context = stream_context_create(array('http' => array( + 'method' => 'POST', + 'header' => "Content-Type: application/x-www-form-urlencoded\r\n", + 'content' => http_build_query(array( + 'secret' => $secret, + 'response' => $response, + 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '', + )), + 'timeout' => 10, + ))); + $reply = @file_get_contents('https://www.google.com/recaptcha/api/siteverify', FALSE, $context); + if ($reply === FALSE) { + error_log('xxx: could not reach the reCAPTCHA server'); + return FALSE; + } + $result = json_decode($reply); + return is_object($result) && !empty($result->success); +} + +/* + * For story submissions: a solved captcha counts for CAPTCHA_PASS_SECONDS + * in this session, so the preview and the submit need only one captcha. + * captcha_used() ends the pass once a story is saved. + */ +function captcha_passed() { + return !empty($_SESSION['captcha_ok']) && $_SESSION['captcha_ok'] > time() - CAPTCHA_PASS_SECONDS; +} + +function captcha_check() { + if (captcha_passed()) + return TRUE; + if (captcha_verify_response()) { + $_SESSION['captcha_ok'] = time(); + return TRUE; + } + return FALSE; +} + +function captcha_used() { + unset($_SESSION['captcha_ok']); +} +?> diff --git a/publish/index.php b/publish/index.php index ea4774d..10acb40 100644 --- a/publish/index.php +++ b/publish/index.php @@ -19,7 +19,7 @@ require_once("../session.lib.php"); - + = captcha_script() ?> 0) { $denied = TRUE; $wait_minutes = (int)ceil($wait / 60); +} else if (isset($_POST['login']) && !captcha_verify_response()) { + $denied = TRUE; + $captcha_failed = TRUE; } else if (isset($_POST['login'])) { include_once('../open_db.lib.php'); $table = "editors"; @@ -76,6 +80,7 @@ if (isset($_SESSION['editor'])) { + = captcha_script() ?> @@ -89,6 +94,8 @@ if (isset($_SESSION['editor'])) { Too many failed logins. Please try again in $wait_minutes minute".($wait_minutes == 1 ? "" : "s").".\n"; +else if ($captcha_failed) + echo "Please complete the captcha.
\n"; else if ($denied) echo "Access denied. Please try again.
\n"; ?> @@ -112,6 +119,8 @@ else if ($denied) + = captcha_widget() ?> +Please review your submission. Once you\'ve finished, please click "Submit" at the bottom of the page, or click "Preview" to check it again.
'; // Keep a new image outside the web root until the story is saved; diff --git a/publish/style.css b/publish/style.css index e7f1108..3dfa5b4 100644 --- a/publish/style.css +++ b/publish/style.css @@ -148,8 +148,8 @@ ul.error-box li { } #main .grid div.g-recaptcha { - overflow: inherit; - margin-left: 120px; + grid-column: 2; + margin: 5px 0; } #captcha #recaptcha_image { margin-left: 120px; diff --git a/publish/write.inc.php b/publish/write.inc.php index 8ef0548..14fa83f 100644 --- a/publish/write.inc.php +++ b/publish/write.inc.php @@ -103,8 +103,9 @@ if (isset($file_src) || isset($img)) {