Files
JTL-Shop-ALTCHA-Spamschutz/fp_altcha_spamschutz/assets/fp-altcha.js
T

149 lines
5.1 KiB
JavaScript
Raw Normal View History

2026-09-21 15:29:02 +02:00
/*!
* fp-altcha.js -- kleiner, eigenständiger Löser für das klassische ALTCHA V1
2026-09-21 15:29:02 +02:00
* Proof-of-Work-Verfahren (SHA-256 Hashcash-Stil).
*
* Sucht für eine vom Server vorgegebene Aufgabe {algorithm, challenge, salt, maxnumber, signature}
* eine Zahl n mit 0 <= n <= maxnumber, für die SHA-256(salt + n) == challenge gilt, und trägt das
2026-09-21 15:29:02 +02:00
* Ergebnis base64-kodiert in ein verstecktes Formularfeld "altcha" ein. Die Verifikation auf dem
* Server erfolgt mit der offiziellen ALTCHA-PHP-Bibliothek (siehe src/Vendor/AltchaOrg/Altcha/V1).
*
* Läuft vollständig lokal im Browser -- es wird keine Anfrage an einen Drittanbieter gesendet.
2026-09-21 15:29:02 +02:00
*/
(function () {
'use strict';
function bufferToHex(buffer) {
var bytes = new Uint8Array(buffer);
var hex = '';
for (var i = 0; i < bytes.length; i++) {
var h = bytes[i].toString(16);
hex += h.length === 1 ? '0' + h : h;
}
return hex;
}
function base64Encode(str) {
var bytes = new TextEncoder().encode(str);
var binary = '';
for (var i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
async function solve(algorithm, salt, target, maxNumber) {
if (algorithm !== 'SHA-256') {
// Nur SHA-256 wird von diesem kleinen Löser unterstützt.
2026-09-21 15:29:02 +02:00
return null;
}
var encoder = new TextEncoder();
for (var n = 0; n <= maxNumber; n++) {
var data = encoder.encode(salt + n);
var digest = await crypto.subtle.digest('SHA-256', data);
if (bufferToHex(digest) === target) {
return n;
}
}
return null;
}
async function processWidget(container) {
var raw = container.getAttribute('data-fp-altcha');
if (!raw) {
return;
}
var task;
try {
task = JSON.parse(raw);
} catch (e) {
return;
}
var status = container.querySelector('.fp-altcha-status');
var input = container.querySelector('.fp-altcha-input');
if (!input) {
return;
}
try {
var number = await solve(task.algorithm, task.salt, task.challenge, task.maxnumber);
if (number === null) {
if (status) {
status.textContent = container.getAttribute('data-label-failed') || 'Sicherheitsprüfung fehlgeschlagen. Bitte Seite neu laden.';
2026-09-21 15:29:02 +02:00
}
container.setAttribute('data-fp-altcha-state', 'failed');
return;
}
var payload = {
algorithm: task.algorithm,
challenge: task.challenge,
number: number,
salt: task.salt,
signature: task.signature
};
input.value = base64Encode(JSON.stringify(payload));
container.setAttribute('data-fp-altcha-state', 'verified');
if (status) {
status.innerHTML = '<i class="fa fa-check" aria-hidden="true"></i> ' +
(container.getAttribute('data-label-verified') || 'Ja, ich bin ein Mensch');
2026-09-21 15:29:02 +02:00
}
} catch (e) {
container.setAttribute('data-fp-altcha-state', 'failed');
if (status) {
status.textContent = container.getAttribute('data-label-failed') || 'Sicherheitsprüfung fehlgeschlagen. Bitte Seite neu laden.';
2026-09-21 15:29:02 +02:00
}
}
}
function guardForm(form) {
form.addEventListener('submit', function (event) {
var widgets = form.querySelectorAll('.fp-altcha');
for (var i = 0; i < widgets.length; i++) {
var state = widgets[i].getAttribute('data-fp-altcha-state');
if (state !== 'verified') {
event.preventDefault();
var status = widgets[i].querySelector('.fp-altcha-status');
if (status) {
status.textContent = widgets[i].getAttribute('data-label-wait') ||
'Bitte einen Moment warten, die Sicherheitsprüfung läuft noch.';
2026-09-21 15:29:02 +02:00
}
return;
}
}
});
}
function init() {
if (!window.crypto || !window.crypto.subtle) {
// Sehr alte Browser ohne Web Crypto API: Prüfung kann nicht durchgeführt werden.
// Das Formular bleibt dann serverseitig blockiert (kein gültiges "altcha" Feld).
2026-09-21 15:29:02 +02:00
return;
}
var containers = document.querySelectorAll('.fp-altcha[data-fp-altcha]');
containers.forEach(function (container) {
processWidget(container);
var form = container.closest('form');
if (form && !form.hasAttribute('data-fp-altcha-guarded')) {
form.setAttribute('data-fp-altcha-guarded', '1');
guardForm(form);
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();