Captcha improvements

This commit is contained in:
Sascha Leib
2025-10-23 20:56:24 +02:00
parent 12993035b5
commit cdc02cd4c3
5 changed files with 286 additions and 25 deletions

View File

@@ -208,14 +208,16 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin {
private function checkCaptchaCookie() {
$cookieVal = isset($_COOKIE['captcha']) ? $_COOKIE['captcha'] : null;
$cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null;
$today = new DateTime();
$isodate = substr((new DateTime())->format('c'), 0, 10);
$today = substr((new DateTime())->format('c'), 0, 10);
$raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $isodate;
$raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today;
$expected = hash('sha256', $raw);
return $cookieVal !== hash('sha256', $raw);
//echo '<ul><li>cookie: ' . $cookieVal . '</li><li>expected: ' . $expected . '</li><li>matches: ' .($cookieVal == $expected ? 'true' : 'false') . '</li></ul>';
return $cookieVal !== $expected;
}
private function insertCaptchaLoader() {

View File

@@ -1,6 +1,6 @@
"use strict";
/* DokuWiki BotMon Captcha JavaScript */
/* 22.10.2025 - 0.1.0 - pre-release */
/* 23.10.2025 - 0.1.2 - pre-release */
/* Author: Sascha Leib <ad@hominem.info> */
const $BMCaptcha = {
@@ -20,20 +20,25 @@ const $BMCaptcha = {
const dlg = document.createElement('dialog');
dlg.setAttribute('closedby', 'none');
dlg.setAttribute('open', 'open');
dlg.classList.add('checking');
dlg.id = 'botmon_captcha_box';
dlg.innerHTML = '<h2>Captcha box</h2><p>Checking if you are a human …</p><p></p>';
dlg.innerHTML = '<h2>Captcha box</h2><p>Making sure you are a human:</p>';
// Checkbox:
const lbl = document.createElement('label');
lbl.innerHTML = '<span class="confirm">Click to confirm.</span><span class="busy"></span><span class="checking">Checking&nbsp;&hellip;</span><span class="loading">Loading&nbsp;&hellip;</span>';
const cb = document.createElement('input');
cb.setAttribute('type', 'checkbox');
cb.setAttribute('disabled', 'disabled');
cb.addEventListener('click', $BMCaptcha._cbCallback);
lbl.appendChild(cb);
lbl.appendChild(document.createTextNode('I am a human.'));
lbl.prepend(cb);
dlg.appendChild(lbl);
bm_parent.appendChild(dlg);
// call the delayed callback in a couple of seconds:
setTimeout($BMCaptcha._delayedCallback, 1500);
},
/* creates a digest hash for the cookie function */
@@ -147,19 +152,42 @@ const $BMCaptcha = {
if (e.target.checked) {
//document.getElementById('botmon_captcha_box').close();
// make a hash for the cookie:
const seed = document._botmon.seed || '';
const extIp = document._botmon.ip || '0.0.0.0';
const d = new Date(document._botmon.t0);
const raw = seed + '|' + location.hostname + '|' + extIp + '|' + d.toISOString().substring(0, 10);
const dat = [ // the data to encode
document._botmon.seed || '',
location.hostname,
document._botmon.ip || '0.0.0.0',
(document._botmon.t0 ? new Date(document._botmon.t0) : new Date()).toISOString().substring(0, 10)
];
const hash = $BMCaptcha.digest.hash(dat.join('|'));
const hash = $BMCaptcha.digest.hash(raw);
console.log('Setting cookie to:', raw, ' --> ', hash);
document.cookie = "captcha=" + hash + ';';
// set the cookie:
document.cookie = "DWConfirm=" + hash + ';path=/;';
// change the interface:
const dlg = document.getElementById('botmon_captcha_box');
if (dlg) {
dlg.classList.remove('ready');
dlg.classList.add('loading');
}
// reload the page:
window.location.reload(true);
}
},
_delayedCallback: function() {
const dlg = document.getElementById('botmon_captcha_box');
if (dlg) {
dlg.classList.remove('checking');
dlg.classList.add('ready');
const input = dlg.getElementsByTagName('input')[0];
if (input) {
input.removeAttribute('disabled');
input.focus();
}
}
},
}
// initialise the captcha module:

139
img/busy-light.svg Normal file
View File

@@ -0,0 +1,139 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
<style>.box{fill:#00CADB;transform-origin: 50% 50%}
@keyframes box-1 {
9% {transform: translate(-12px,0)}
18% {transform: translate(0px,0)}
27% {transform: translate(0px,0)}
36% {transform: translate(12px,0)}
45% {transform: translate(12px,12px)}
55% {transform: translate(12px,12px)}
64% {transform: translate(12px,12px)}
73% {transform: translate(12px,0px)}
82% {transform: translate(0px,0px)}
91% {transform: translate(-12px,0px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(1){animation: box-1 4s infinite}
@keyframes box-2 {
9% {transform: translate(0,0)}
18% {transform: translate(12px,0)}
27% {transform: translate(0px,0)}
36% {transform: translate(12px,0)}
45% {transform: translate(12px,12px)}
55% {transform: translate(12px,12px)}
64% {transform: translate(12px,12px)}
73% {transform: translate(12px,12px)}
82% {transform: translate(0px,12px)}
91% {transform: translate(0px,12px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(2){animation: box-2 4s infinite}
@keyframes box-3 {
9% {transform: translate(-12px,0)}
18% {transform: translate(-12px,0)}
27% {transform: translate(0px,0)}
36% {transform: translate(-12px,0)}
45% {transform: translate(-12px,0)}
55% {transform: translate(-12px,0)}
64% {transform: translate(-12px,0)}
73% {transform: translate(-12px,0)}
82% {transform: translate(-12px,-12px)}
91% {transform: translate(0px,-12px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(3) {animation: box-3 4s infinite}
@keyframes box-4 {
9% {transform: translate(-12px,0)}
18% {transform: translate(-12px,0)}
27% {transform: translate(-12px,-12px)}
36% {transform: translate(0px,-12px)}
45% {transform: translate(0px,0px)}
55% {transform: translate(0px,-12px)}
64% {transform: translate(0px,-12px)}
73% {transform: translate(0px,-12px)}
82% {transform: translate(-12px,-12px)}
91% {transform: translate(-12px,0px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(4) {animation: box-4 4s infinite}
@keyframes box-5 {
9% {transform: translate(0,0)}
18% {transform: translate(0,0)}
27% {transform: translate(0,0)}
36% {transform: translate(12px,0)}
45% {transform: translate(12px,0)}
55% {transform: translate(12px,0)}
64% {transform: translate(12px,0)}
73% {transform: translate(12px,0)}
82% {transform: translate(12px,-12px)}
91% {transform: translate(0px,-12px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(5) {animation: box-5 4s infinite}
@keyframes box-6 {
9% {transform: translate(0,0)}
18% {transform: translate(-12px,0)}
27% {transform: translate(-12px,0)}
36% {transform: translate(0px,0)}
45% {transform: translate(0px,0)}
55% {transform: translate(0px,0)}
64% {transform: translate(0px,0)}
73% {transform: translate(0px,12px)}
82% {transform: translate(-12px,12px)}
91% {transform: translate(-12px,0px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(6) {animation: box-6 4s infinite}
@keyframes box-7 {
9% {transform: translate(12px,0)}
18% {transform: translate(12px,0)}
27% {transform: translate(12px,0)}
36% {transform: translate(0px,0)}
45% {transform: translate(0px,-12px)}
55% {transform: translate(12px,-12px)}
64% {transform: translate(0px,-12px)}
73% {transform: translate(0px,-12px)}
82% {transform: translate(0px,0px)}
91% {transform: translate(12px,0px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(7) {animation: box-7 4s infinite}
@keyframes box-8 {
9% {transform: translate(0,0)}
18% {transform: translate(-12px,0)}
27% {transform: translate(-12px,-12px)}
36% {transform: translate(0px,-12px)}
45% {transform: translate(0px,-12px)}
55% {transform: translate(0px,-12px)}
64% {transform: translate(0px,-12px)}
73% {transform: translate(0px,-12px)}
82% {transform: translate(12px,-12px)}
91% {transform: translate(12px,0px)}
100% {transform: translate(0px,0px)}
}
.box:nth-child(8){animation: box-8 4s infinite}
@keyframes box-9{
9% {transform: translate(-12px,0)}
18% {transform: translate(-12px,0)}
27% {transform: translate(0px,0)}
36% {transform: translate(-12px,0)}
45% {transform: translate(0px,0)}
55% {transform: translate(0px,0)}
64% {transform: translate(-12px,0)}
73% {transform: translate(-12px,0)}
82% {transform: translate(-24px,0)}
91% {transform: translate(-12px,0)}
100% {transform: translate(0px,0)}
}
.box:nth-child(9) {animation: box-9 4s infinite}
</style>
<g><rect class="box" x="13" y="1" rx="1" width="10" height="10"/>
<rect class="box" x="13" y="1" rx="1" width="10" height="10"/>
<rect class="box" x="25" y="25" rx="1" width="10" height="10"/>
<rect class="box" x="13" y="13" rx="1" width="10" height="10"/>
<rect class="box" x="13" y="13" rx="1" width="10" height="10"/>
<rect class="box" x="25" y="13" rx="1" width="10" height="10"/>
<rect class="box" x="1" y="25" rx="1" width="10" height="10"/>
<rect class="box" x="13" y="25" rx="1" width="10" height="10"/>
<rect class="box" x="25" y="25" rx="1" width="10" height="10"/></g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -520,7 +520,6 @@ road 8
late 8
stand 8
suppose 8
la 8
daughter 8
real 8
nearly 8
@@ -1955,7 +1954,6 @@ ends 2
shop 2
stairs 2
pardon 2
gay 2
beg 2
seldom 2
kinds 2

View File

@@ -11,14 +11,94 @@ body.botmon_captcha {
}
}
#botmon_captcha_box {
border: red dotted 2pt;
& {
position: fixed;
width: 400px;
height: 400px;
top: ~"calc(50vh - 200px)";
height: 220px;
top: ~"calc(50vh - 110px)";
left: ~"calc(50vw - 200px)";
border-radius: .5rem;
margin: 0; padding: .5rem;
background: #1C1B22 none;
border: #7C7B82 solid 1pt;
border-radius: 12px;
margin: 0; padding: 18px;
font-family: system-ui, sans-serif;
box-shadow: .25rem .25rem .5rem rgba(0,0,0,.5);
box-sizing: border-box;
}
& * {
color: #EDEDF5;
margin: 0;
}
h2 {
font-size: 24px;
line-height: 32px;
padding: 0 0 12px 0;
}
p {
font-size: 16px;
line-height: 20px;
padding: 6px 0;
}
label {
& {
display: grid;
grid-template-columns: 32px auto;
column-gap: 8px;
align-items: center;
padding: 24px;
margin: 16px 0 0 0;
background-color: #32313A;
border: #7C7B82 solid 1px;
border-radius: 3px;
font-size: 16px;
}
span.busy {
display: inline-block;
width: 24px; height: 24px;
background: transparent url('img/busy-light.svg') center no-repeat;
background-size: 24px;
}
}
input[type="checkbox"] {
width: 24px; height: 24px;
border: 2px solid #00CADB;
border-radius: 4px;
appearance: none;
}
input[type="checkbox"]:focus {
outline: none;
box-shadow: 0 0 6px rgba(0, 202, 219, .8);
}
input[type="checkbox"]:disabled {
display: none;
}
&.checking {
input[type="checkbox"], span.confirm, span.loading { display: none;}
span.busy, span.checking { display: initial; }
label, input[type="checkbox"] { cursor: none; }
}
&.ready {
input[type="checkbox"], span.confirm { display: initial;}
span.busy, span.checking, span.loading { display: none; }
label, input[type="checkbox"] { cursor: pointer; }
}
&.loading {
span.busy, span.loading { display: initial; }
input[type="checkbox"], span.confirm, span.checking { display: none;}
label { cursor: busy; }
}
}
}
// smaller screens:
@media (max-width: 480px) {
body.botmon_captcha #botmon_captcha_box {
width: 100vw;
height: auto;
left: 0; top: 80px;
border-radius: 2px;
z-index: 10001;
}
}
@@ -33,5 +113,19 @@ body.botmon_captcha {
text-shadow: 0 0 .35em rgba(170,170,170,.75);
}
}
#botmon_captcha_box {
& {
background-color: #FFF;
border-color: #9F9EA1;
box-shadow: .25rem .25rem .5rem rgba(0,0,0,.25);
}
& * {
color: #15141A;
}
label {
background-color: #EEE;
border-color: #9F9EA1;
}
}
}
}