Bug 39906: Add a simple quick Apache-level bot challenge

This patch adds a fast simple bot challenge via Apache.

To test:
0. Apply the patch
1. sudo cp debian/templates/apache-shared-opac-antibot.conf /etc/koha/.
2. sudo cp debian/templates/apache-shared-opac-plack.conf /etc/koha/.
3. curl http://localhost:8080/cgi-bin/koha/opac-search.pl
4. Note an instant response with a very simple HTML response
5. Open an incognito browser and go to http://localhost:8080/cgi-bin/koha/opac-search.pl
6. Note a very brief loading animation before the OPAC search page appearing
7. Do a search
8. Note no more loading animation appears
9. Close the incognito browser
10. Open an incognito browser and go to http://localhost:8080/cgi-bin/koha/opac-main.pl
11. Do a search via the "Library catalog" search bar
12. Note no loading animation appears

Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
Signed-off-by: Cornelius Hertfelder <cornelius.hertfelder@bsz-bw.de>
Signed-off-by: Magnus Enger <magnus@libriotech.no>
We have tested this in production on at least three live sites for
several weeks, and it has worked as expected. We have not noticed any
negative side effects.

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
David Cook 2025-05-15 06:39:34 +00:00 committed by Lucas Gass
parent 50c9ec4340
commit 518c64a6dd
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
5 changed files with 92 additions and 0 deletions

View File

@ -0,0 +1,14 @@
RewriteEngine on
#NOTE: To override this setting of ANTIBOT_DO, in the parent VirtualHost add the following: SetEnvIf Request_URI ^ ANTIBOT_OVERRIDE=true
#You can then provide your own conditions for setting the ANTIBOT_DO environmental variable
RewriteCond %{ENV:ANTIBOT_OVERRIDE} ^$
RewriteCond expr "%{REQUEST_URI} =~ m#^/cgi-bin/koha/(opac-detail.pl|opac-export.pl|opac-suggestions.pl|opac-search.pl|opac-authoritiesdetail.pl|opac-ISBDdetail.pl|opac-MARCdetail.pl|opac-shelves.pl)$#"
RewriteCond %{HTTP:Cookie} !(^|;\s*)CGISESSID= [NC]
#NOTE: The KOHA_INIT is set by Javascript by the fast challenge webpage
RewriteCond %{HTTP:Cookie} !(^|;\s*)KOHA_INIT= [NC]
RewriteRule ^ - [E=ANTIBOT_DO:true]
#NOTE: Internal redirect to our challenge page
RewriteCond %{ENV:ANTIBOT_DO} ^true$
RewriteRule ^ /opac-tmpl/lib/koha_fast_challenge/index.html [L,PT]

View File

@ -13,6 +13,8 @@
RequestHeader set X-FORWARDED-PROTO "https" env=HTTPS
Include "/etc/koha/apache-shared-opac-antibot.conf"
# Point the OPAC site to Plack
ProxyPass /cgi-bin/koha "unix:/var/run/koha/${instance}/plack.sock|http://localhost/opac"
ProxyPassReverse /cgi-bin/koha "unix:/var/run/koha/${instance}/plack.sock|http://localhost/opac"

View File

@ -0,0 +1,11 @@
<html>
<head>
<link href="/opac-tmpl/lib/koha_fast_challenge/style.css" rel="stylesheet"/>
<script src="/opac-tmpl/lib/koha_fast_challenge/index.js"></script>
</head>
<body>
<div class="loading-overlay">
<div class="spinner"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,34 @@
document.addEventListener("DOMContentLoaded", event => {
const timeout_base = 0;
let timeout_incr = 250;
if (navigator.webdriver){
timeout_incr += 10000
}
if (navigator.plugins.length === 0){
timeout_incr += 5000;
}
if (navigator.mimeTypes.length === 0){
timeout_incr += 5000;
}
if (!navigator.languages || navigator.languages.length === 0){
timeout_incr += 5000;
}
const timestamp1 = performance.now();
requestAnimationFrame(() => {
const delay = performance.now() - timestamp1;
if (delay < 20){
timeout_incr += 1000;
}
});
let final_timeout = timeout_base + timeout_incr;
setTimeout(() => {
let koha_init_cookie = "KOHA_INIT=1; path=/; SameSite=Lax";
if (location.protocol === 'https:'){
koha_init_cookie += "; Secure";
}
document.cookie = koha_init_cookie;
location.reload();
}, final_timeout);
});

View File

@ -0,0 +1,31 @@
.loading-overlay {
display: flex;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
z-index: 9999;
pointer-events: auto;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(255, 255, 255, 0.2);
border-top: 5px solid white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}