AltchaService.php hinzufuegen
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Plugin\fp_altcha_spamschutz\src\Service;
|
||||
|
||||
use AltchaOrg\Altcha\V1\Altcha;
|
||||
use AltchaOrg\Altcha\V1\ChallengeOptions;
|
||||
use AltchaOrg\Altcha\V1\Hasher\Algorithm;
|
||||
use JTL\Plugin\PluginInterface;
|
||||
|
||||
/**
|
||||
* Kapselt die ALTCHA V1 Proof-of-Work Bibliothek (MIT-Lizenz, https://github.com/altcha-org/altcha-lib-php).
|
||||
*
|
||||
* Warum V1 statt der aktuellen ALTCHA-Widget-Version (v3)?
|
||||
* Das offizielle JS-Widget v3 nutzt ein neueres, komplexeres Protokoll (PBKDF2/Argon2id/Scrypt mit
|
||||
* Key-Prefix-Matching), fuer das es keine einfache, robust dokumentierte "Challenge direkt einbetten"-
|
||||
* Variante ohne zusaetzlichen Netzwerk-Endpunkt gibt. Das klassische V1-Protokoll (SHA-256 Hashcash:
|
||||
* Client sucht per Brute-Force eine Zahl n, fuer die SHA256(salt+n) == challenge gilt) ist dagegen
|
||||
* vollstaendig dokumentiert, einfach zu pruefen und wird hier zusammen mit einem kleinen eigenen
|
||||
* JS-Loeser (assets/fp-altcha.js) eingesetzt. Serverseitig kommt weiterhin der offizielle,
|
||||
* unveraenderte ALTCHA-Code zum Einsatz (siehe src/Vendor/AltchaOrg/Altcha/V1).
|
||||
*/
|
||||
class AltchaService
|
||||
{
|
||||
private PluginInterface $plugin;
|
||||
private Altcha $altcha;
|
||||
|
||||
public function __construct(PluginInterface $plugin)
|
||||
{
|
||||
$this->plugin = $plugin;
|
||||
$this->altcha = new Altcha($this->getHmacSecret());
|
||||
}
|
||||
|
||||
public function isConfigured(): bool
|
||||
{
|
||||
return $this->getHmacSecret() !== '';
|
||||
}
|
||||
|
||||
public function isEnabledForRegistration(): bool
|
||||
{
|
||||
return $this->getConfigValue('fp_altcha_protect_register', 'on') === 'on';
|
||||
}
|
||||
|
||||
public function isEnabledForNewsletter(): bool
|
||||
{
|
||||
return $this->getConfigValue('fp_altcha_protect_newsletter', 'on') === 'on';
|
||||
}
|
||||
|
||||
public function isDebug(): bool
|
||||
{
|
||||
return $this->getConfigValue('fp_altcha_debug', '') === 'on';
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt eine neue Pruefung und liefert sie als Array, das 1:1 als JSON in die Seite
|
||||
* eingebettet werden kann (siehe TemplateHandler).
|
||||
*
|
||||
* @return array<string, string|int>
|
||||
*/
|
||||
public function createChallengeArray(): array
|
||||
{
|
||||
$maxNumber = (int) $this->getConfigValue('fp_altcha_max_number', '150000');
|
||||
if ($maxNumber < 1000) {
|
||||
$maxNumber = 150000;
|
||||
}
|
||||
|
||||
$expirySeconds = (int) $this->getConfigValue('fp_altcha_expiry_seconds', '600');
|
||||
if ($expirySeconds < 30) {
|
||||
$expirySeconds = 600;
|
||||
}
|
||||
|
||||
$expires = new \DateTimeImmutable('+' . $expirySeconds . ' seconds');
|
||||
|
||||
$challenge = $this->altcha->createChallenge(new ChallengeOptions(
|
||||
algorithm: Algorithm::SHA256,
|
||||
maxNumber: $maxNumber,
|
||||
expires: $expires,
|
||||
));
|
||||
|
||||
return [
|
||||
'algorithm' => $challenge->algorithm,
|
||||
'challenge' => $challenge->challenge,
|
||||
'maxnumber' => $challenge->maxNumber,
|
||||
'salt' => $challenge->salt,
|
||||
'signature' => $challenge->signature,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prueft das per POST["altcha"] gesendete, base64-kodierte Loesungs-Payload.
|
||||
*/
|
||||
public function verifyPost(): bool
|
||||
{
|
||||
$field = $_POST['altcha'] ?? null;
|
||||
|
||||
if (!\is_string($field) || $field === '') {
|
||||
$this->log('kein altcha Feld im POST gefunden');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isConfigured()) {
|
||||
// Kein HMAC-Secret hinterlegt -> Plugin ist nicht korrekt eingerichtet.
|
||||
// Sicherheitshalber ablehnen statt durchzulassen.
|
||||
$this->log('kein HMAC-Secret konfiguriert, Pruefung wird abgelehnt');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$verified = $this->altcha->verifySolution($field, true);
|
||||
$this->log('Pruefungsergebnis: ' . ($verified ? 'erfolgreich' : 'fehlgeschlagen'));
|
||||
|
||||
return $verified;
|
||||
}
|
||||
|
||||
private function getHmacSecret(): string
|
||||
{
|
||||
return $this->getConfigValue('fp_altcha_hmac_secret', '');
|
||||
}
|
||||
|
||||
private function getConfigValue(string $name, string $default): string
|
||||
{
|
||||
$value = $this->plugin->getConfig()->getValue($name);
|
||||
|
||||
if (!\is_string($value) || $value === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function log(string $message): void
|
||||
{
|
||||
if (!$this->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
error_log('[fp_altcha_spamschutz] ' . $message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user