Implemented answer scrambling and heavy JS code obfuscation.

This commit is contained in:
Janek Bevendorff 2012-08-16 00:44:49 +02:00
parent 8faa95104c
commit 8bd86e5783
5 changed files with 259 additions and 33 deletions

View file

@ -46,7 +46,7 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_CUSTOM', 'Eigene Fragen');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DESC', 'Fortgeschrittene Konfigurationsoptionen für das verstecke Captcha. Wenn dieses nicht aktiviert ist, kann dieser Abschnitt getrost übersprungen werden.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL', 'Methode für Abfrage der Antwort');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL_DESC', 'Wähle hier aus, wie die korrekte Antwort abgefragt werden soll. Bei Auswahl von "JSON" kann ein Ajax-Request an index.php/plugin/spamblockbeecaptcha abgesetzt werden, um die richtige Antwort zu erhalten. Die Auswahl "Smarty" wird die Antwort über die Smarty-Variable {$beeCaptchaAnswer} bereitstellen, während "Standard" den Wert in der Seite hartkodiert. ACHTUNG: ist "Smarty" ausgewählt, wird keinerlei zusätzliches CSS oder JavaScript eingebunden. Das Feld muss also selbst befüllt und versteckt werden.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL_DESC', 'Wähle hier aus, wie die korrekte Antwort abgefragt werden soll. Bei Auswahl von "JSON" kann ein Ajax-Request an index.php/plugin/spamblockbeecaptcha abgesetzt werden, um die richtige Antwort zu erhalten. Die Auswahl "Smarty" wird die Antwort über die Smarty-Variable {$beeCaptchaAnswer} bereitstellen, während "Standard" den Wert in der Seite hartkodiert. ACHTUNG: ist "Smarty" ausgewählt, wird keinerlei zusätzliches CSS oder JavaScript eingebunden. Das Captcha-Feld muss also selbst befüllt und versteckt werden.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE', 'Art der Frage');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE_DESC', 'Spamschutz Biene kann automatisch simple Rechenaufgaben generieren. Es können aber auch eigene Fragen und Antworten angegeben werden. Wähle aus, was du bevorzugst.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTIONS', 'Eigene Fragen');
@ -57,6 +57,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_ANSWERS', "Antwort1\nAntwort2");
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP', 'Reguläre Ausdrücke benutzen');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP_DESC', 'Gibt an, ob Perl-kompatible reguläre Ausdrücke (PCREs) für die Antworten verwendet werden sollen. Diese können dazu benutzt werden, mehrere Varianten einer Antwort zuzulassen. Jeder Antwortzeile sollte dabei dem Muster /pattern/:Antwort entsprechen. ACHTUNG: Aktiviere diese Option nur, wenn du weißt, was du tust. Ein ungültiger regulärer Ausdruck wird Validitäts-Prüfungen fehlschlagen lassen und könnte dein Blog in wenigen Fällen einer sogenannten Denial-of-Service-Attacke aussetzen. Antworten länger als 1000 Zeichen werden abgewiesen, wenn reguläre Ausdrücke eingeschaltet sind.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE', 'JavaScript verschleiern');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_DESC', 'In einigen wenigen Fällen kann eine JavaScript-Verschleierung die Effektivität des versteckten Captchas erhöhen, indem es Spam-Bots davon abhält, die korrekte Antwort durch eine simple String-Pattern-Suche aus dem Code zu extrahieren. Es stehen zwei Verschleierungs-Methoden zur Verfügung: "Verschleiere Antwort" verschleiert bloß die Antwort durch eine simple XOR-Verschlüsselung anstatt sie als Klartext in das HTML-Dokument zu schreiben. "Verschleiere Antwort und Code" verschleiert auch noch den JavaScript-Code, um ihn schwerer verständlich zu machen (dies funktioniert nur mit der Standard-Methode zur Abfrage der Antwort). Bei Verwendung der Methode "Smarty" enthält die Variable {$beeCaptchaScrambleKey} den Entschlüsselungs-Key. Denke daran, dass die verschlüsselte Antwort URL-enkodiert werden muss, bevor du sie ins HTML-Dokument schreiben kannst!');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_OFF', 'Nicht verschleiern');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS', 'Verschleiere Antwort');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS_AND_CODE', 'Verschleiere Antwort und Code');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_0', 'Null');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'Eins');

View file

@ -57,6 +57,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_ANSWERS', "Antwort1\nAntwort2");
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP', 'Reguläre Ausdrücke benutzen');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP_DESC', 'Gibt an, ob Perl-kompatible reguläre Ausdrücke (PCREs) für die Antworten verwendet werden sollen. Diese können dazu benutzt werden, mehrere Varianten einer Antwort zuzulassen. Jeder Antwortzeile sollte dabei dem Muster /pattern/:Antwort entsprechen. ACHTUNG: Aktiviere diese Option nur, wenn du weißt, was du tust. Ein ungültiger regulärer Ausdruck wird Validitäts-Prüfungen fehlschlagen lassen und könnte dein Blog in wenigen Fällen einer sogenannten Denial-of-Service-Attacke aussetzen. Antworten länger als 1000 Zeichen werden abgewiesen, wenn reguläre Ausdrücke eingeschaltet sind.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE', 'JavaScript verschleiern');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_DESC', 'In einigen wenigen Fällen kann eine JavaScript-Verschleierung die Effektivität des versteckten Captchas erhöhen, indem es Spam-Bots davon abhält, die korrekte Antwort durch eine simple String-Pattern-Suche aus dem Code zu extrahieren. Es stehen zwei Verschleierungs-Methoden zur Verfügung: "Verschleiere Antwort" verschleiert bloß die Antwort durch eine simple XOR-Verschlüsselung anstatt sie als Klartext in das HTML-Dokument zu schreiben. "Verschleiere Antwort und Code" verschleiert auch noch den JavaScript-Code, um ihn schwerer verständlich zu machen (dies funktioniert nur mit der Standard-Methode zur Abfrage der Antwort). Bei Verwendung der Methode "Smarty" enthält die Variable {$beeCaptchaScrambleKey} den Entschlüsselungs-Key. Denke daran, dass die verschlüsselte Antwort URL-enkodiert werden muss, bevor du sie ins HTML-Dokument schreiben kannst!');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_OFF', 'Nicht verschleiern');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS', 'Verschleiere Antwort');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS_AND_CODE', 'Verschleiere Antwort und Code');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_0', 'Null');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'Eins');

View file

@ -47,7 +47,7 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_MATH', 'Math problems');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_CUSTOM', 'Custom questions');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DESC', 'Advanced configuration options for the hidden Captcha. If the captcha is disabled, you can safely ignore this section.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL', 'Answer Retrieval Method');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL', 'Answer retrieval method');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWER_RETRIEVAL_DESC', 'Select how you want to retrieve to correct answer to the Captcha. If you select "JSON", you can send an Ajax request to index.php/plugin/spamblockbeecaptcha to get the answer. "Smarty" will provide the answer through the Smarty variable {$beeCaptchaAnswer}, whereas "Default" will hard code it into the page. NOTE: If "Smarty" is selected, no additional CSS or JavaScript will be included. You have to fill and hide the Captcha field yourself.');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE', 'Type of question');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QUESTION_TYPE_DESC', 'Spamblock Bee can automatically generate simple math problems for you or you can create your own questions and answers. Select which one you prefer');
@ -59,6 +59,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_DEFAULT_ANSWERS', "Answer1\nAnswer2");
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP', 'Use regular expressions');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_USE_REGEXP_DESC', "Whether to interpret the answers given above as Perl compatible regular expressions (PCREs). This can be used to allow several variants of an answer. Each answer line should follow the rule /pattern/:answer. NOTE: Only enable this if you know what you\'re doing. Filling in bad regular expressions causes validity checks to fail and in some rare cases might expose yourself to a so called Denial of Service attack! Answers longer than 1000 characters will be rejected when regular expression matching is on.");
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE', 'Obfuscate JavaScript');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_DESC', 'In some rare cases JavaScript obfuscation can make the hidden Captcha more effective by preventing spam bots from performing simple string pattern searches in order to extract the correct answer from the code. There are two methods of obfuscation: "Scramble Answer" will only scramble the answer with a simple XOR cipher instead of writing it in plain text to the HTML document. "Scramble Answer and Code" will also obfuscate the code to make it harder to understand (this only works with the "Default" answer retrieval method). If you are using the method "Smarty", the variable {$beeCaptchaScrambleKey} will contain the decryption key. Remember to URL encode the scrambled answer before writing it to the HTML document!');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_OFF', 'Don\'t obfuscate');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS', 'Scramble Answer');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS_AND_CODE', 'Scramble Answer and Code');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_0', 'zero');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'one');

View file

@ -3,9 +3,10 @@
var that = this;
var inputCaptcha = document.getElementById("bee_captcha");
var divCaptcha = document.getElementById('serendipity_comment_beecaptcha');
var method = loadData.method == 'json' ? loadData.method : 'default';
var url = typeof loadData.url != 'undefined' ? loadData.url : null;
var answer = typeof loadData.answer != 'undefined' ? loadData.answer : null;
var method = loadData.method == 'json' ? loadData.method : 'default';
var url = typeof loadData.url != 'undefined' ? loadData.url : null;
var answer = typeof loadData.answer != 'undefined' ? loadData.answer : null;
var scrambleKey = typeof loadData.scrambleKey != 'undefined' ? loadData.scrambleKey : null;
this.attachToLoadEvent = function() {
var handlerCalled = false;
@ -14,7 +15,7 @@
if (handlerCalled) return;
handlerCalled = true;
that.fillCaptcha();
that.initCaptcha();
// We don't need any additional load events anymore
if (document.addEventListener) {
@ -39,14 +40,15 @@
}
}
this.fillCaptcha = function() {
if ('default' == method && null !== answer) {
inputCaptcha.value = answer;
this.hideBeeElement();
this.initCaptcha = function() {
if (null === inputCaptcha) {
return;
}
if ('default' == method && null !== answer) {
fillCaptcha(answer, scrambleKey)
} else if ('json' == method && null !== url) {
fetchJsonData();
return;
}
}
@ -57,6 +59,15 @@
}
}
function fillCaptcha(answer, scrambleKey) {
if (typeof scrambleKey != 'undefined' && null !== scrambleKey) {
answer = xorDescramble(decodeUtf8(unescape(answer)), scrambleKey);
}
inputCaptcha.value = answer;
that.hideBeeElement();
}
function fetchJsonData() {
if (window.XMLHttpRequest) { // Mozilla, Safari, Opera, IE7
var httpRequest = new XMLHttpRequest();
@ -70,22 +81,35 @@
httpRequest.setRequestHeader('content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
httpRequest.send();
}
function fetchJsonDataReady(httpRequest){
function fetchJsonDataReady(httpRequest) {
if (null !== httpRequest && 4 == httpRequest.readyState && 200 == httpRequest.status) {
var response = httpRequest.responseText;
var jsonResponse = (typeof JSON != 'undefined') ? JSON.parse(response) : eval('(' + response + ')');
var jsonResponse = typeof JSON != 'undefined' ? JSON.parse(response) : eval('(' + response + ')');
var answer = jsonResponse.answer;
var scrambleKey = typeof jsonResponse.scrambleKey != 'undefined' ? jsonResponse.scrambleKey : null;
if (typeof answer != 'string' || 'ERROR' != answer.toUpperCase()) {
inputCaptcha.value = answer;
that.hideBeeElement();
fillCaptcha(answer, scrambleKey);
}
}
}
function decodeUtf8(string) {
return decodeURIComponent(escape(string));
}
function xorDescramble(string, key) {
var decoded = '';
for (i = 0; i < string.length; ++i) {
decoded += String.fromCharCode(string.charCodeAt(i) ^ key);
}
return decoded;
}
}
var spamBeeObj = new SpamBeeCaptcha(spamBeeData);
spamBeeObj.attachToLoadEvent();
})();

View file

@ -74,6 +74,18 @@ class serendipity_event_spamblock_bee extends serendipity_event
*/
var $useRegularExpressions = false;
/**
* Whether to scramble the Captcha answer
* @var boolean
*/
var $captchaScrambleAnswer = false;
/**
* Whether to obfuscate the JS code for the hidden Captcha
* @var boolean
*/
var $captchaObfuscateCode = false;
/**
* Constructor. Initialize class variables from configuration
@ -86,6 +98,13 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->hiddenCaptchaHandle = $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE);
$this->useRegularExpressions = $this->get_config('use_regexp', false);
$obfuscate = $this->get_config('obfuscate_answer', 'off');
if ($obfuscate == 'scramble_answer') {
$this->captchaScrambleAnswer = true;
} else if ($obfuscate == 'scramble_answer_and_js') {
$this->captchaScrambleAnswer = true;
$this->captchaObfuscateCode = true;
}
}
/**
@ -123,7 +142,10 @@ class serendipity_event_spamblock_bee extends serendipity_event
$configuration =array_merge($configuration, array('entrytitle', 'samebody', 'required_fields'));
}
$configuration =array_merge($configuration, array('spamlogtype', 'spamlogfile', 'plugin_path'));
$configuration =array_merge($configuration, array('advanced_cc_desc', 'answer_retrieval_method', 'question_type', 'questions', 'answers', 'use_regexp'));
$configuration =array_merge($configuration, array(
'advanced_cc_desc', 'answer_retrieval_method', 'question_type',
'questions', 'answers', 'use_regexp', 'obfuscate_answer'
));
$propbag->add('configuration', $configuration );
$propbag->add('config_groups', array(
@ -131,7 +153,8 @@ class serendipity_event_spamblock_bee extends serendipity_event
'spamlogtype', 'spamlogfile', 'plugin_path'
),
PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SECTION_ADVANCED => array(
'advanced_cc_desc', 'answer_retrieval_method', 'question_type', 'questions', 'answers', 'use_regexp'
'advanced_cc_desc', 'answer_retrieval_method', 'question_type',
'questions', 'answers', 'use_regexp', 'obfuscate_answer'
)
)
);
@ -174,6 +197,12 @@ class serendipity_event_spamblock_bee extends serendipity_event
'custom' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_QT_CUSTOM
);
$obfuscateAnswer = array(
'off' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_OFF,
'scramble_answer' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS,
'scramble_answer_and_js' => PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_SCRAMBLE_JS_AND_CODE
);
switch($name) {
case 'header_desc':
$propbag->add('type', 'content');
@ -290,6 +319,14 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('default', false);
break;
case 'obfuscate_answer':
$propbag->add('type', 'select');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE);
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_OBFUSCATE_DESC);
$propbag->add('select_values', $obfuscateAnswer);
$propbag->add('default', 'off');
break;
default:
return false;
}
@ -550,11 +587,17 @@ class serendipity_event_spamblock_bee extends serendipity_event
* @return string The generated JSON string
*/
function produceCaptchaAnswerJson() {
$answer = $this->getCaptchaAnswer();
if (null === $answer['answer']) {
$answer='ERROR';
$answer = $this->getCaptchaAnswer();
$scrambleKey = rand();
if (!isset($answer['answer'])) {
$answer = array('answer' => 'ERROR');
} else if ($this->captchaScrambleAnswer) {
$answer['answer'] = rawurlencode($this->xorScramble($answer['answer'], $scrambleKey));
$answer['scrambleKey'] = $scrambleKey;
}
return json_encode(array('answer' => $answer['answer']));
return json_encode($answer);
}
/**
@ -588,6 +631,12 @@ class serendipity_event_spamblock_bee extends serendipity_event
if ($this->answerRetrievalMethod == 'smarty') {
$answer = $this->getCaptchaAnswer();
if ($this->captchaScrambleAnswer) {
$scrambleKey = rand();
$answer['answer'] = $this->xorScramble($answer['answer'], $scrambleKey);
$serendipity['smarty']->assign('beeCaptchaScrambleKey', $scrambleKey);
}
$serendipity['smarty']->assign('beeCaptchaAnswer', $answer['answer']);
}
}
@ -605,25 +654,98 @@ class serendipity_event_spamblock_bee extends serendipity_event
global $serendipity;
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$path = $this->path = $this->get_config('plugin_path', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/');
$answer = $this->getCaptchaAnswer();
$answer = $answer['answer'];
$path = $this->path = $this->get_config('plugin_path', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/');
$answer = $this->getCaptchaAnswer();
$answer = $answer['answer'];
$jsProperties = array('method' => $this->answerRetrievalMethod);
echo '<script> var spamBeeData = {';
if ($this->answerRetrievalMethod == 'json') {
echo "'url': '" . $serendipity['baseURL'] . "index.php/plugin/spamblockbeecaptcha', " .
"'method': 'json'";
$jsProperties['url'] = $serendipity['baseURL'] . 'index.php/plugin/spamblockbeecaptcha';
} else {
echo "'answer': " . (is_numeric($answer) ? $answer : "'" . trim(addcslashes($answer, '\\"\'')) . "'") . ', ' .
"'method': 'default'";
$scrambleKey = rand();
if ($this->captchaScrambleAnswer) {
$answer = rawurlencode($this->xorScramble($answer, $scrambleKey));
$jsProperties['scrambleKey'] = $scrambleKey;
}
$jsProperties['answer'] = is_numeric($answer) ? $answer : trim($answer);
}
echo '};</script>' . "\n" .
'<script type="text/javascript" src="' . $path . 'serendipity_event_spamblock_bee.js"></script>';
if ($this->answerRetrievalMethod == 'default' && $this->captchaObfuscateCode) {
// Do some weird obfuscation stuff to the JS code
$spamBeeVar = $this->generateUniqueVarName(array());
// Shuffle array but preserve keys
$jsPropertiesKeys = array_keys($jsProperties);
shuffle($jsPropertiesKeys);
$jsProperties = array_merge(array_flip($jsPropertiesKeys) , $jsProperties);
echo '<script>var spamBeeData = function() { var ' . $spamBeeVar . ' = {};';
$jsVars = array();
$existingKeys = array($spamBeeVar);
foreach($jsProperties as $property => $value) {
$varName = $this->generateUniqueVarName($existingKeys);
$jsVars[$varName] = $property;
$existingKeys[] = $varName;
// URL encode all characters to make values appear almost equal
$encVal = '';
$valLength = mb_strlen($value);
for ($i = 0; $i < $valLength; ++$i) {
$encVal .= '%' . bin2hex(mb_substr($value, $i, 1));
}
echo 'var ' . $varName . " = unescape('" . $encVal . "');";
}
foreach ($jsVars as $var => $value) {
echo $spamBeeVar . "['" . $value . "'] = " . $var . ';';
}
echo 'return ' . $spamBeeVar . '; }();';
echo '</script>';
} else {
echo '<script>var spamBeeData = ' . json_encode($jsProperties) . ';</script>' . "\n";
}
echo '<script src="' . $path . 'serendipity_event_spamblock_bee.js"></script>';
}
}
/**
* Generate a unique random variable name. Used for generating obfuscated
* JS code. To make sure, the name is really unique, pass an array of all
* variable names already existing to this function.
* Returns an empty string if no unique variable name could be generated.
*
* @param array $existingVarNames
* @param int $length
* @return string
*/
function generateUniqueVarName($existingVarNames, $length = 5) {
$varName = '';
$pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
$attempts = 0;
for ($i = 0; $i < $length; ++$i) {
$varName .= $pool[rand(0, strlen($pool))];
// If variable name has been generated, but is not unique, start over again
if ($i == ($length - 1) && in_array($varName, $existingVarNames)) {
// If we already have 10 attempts, give up and return empty string (should not happen)
if ($attempts >= 9) {
return '';
}
$i = 0;
++$attempts;
}
}
return $varName;
}
/**
* If retrieval method != 'json' and the hidden Captcha is enabled, print
* the needed CSS for hiding it to the output buffer.
@ -857,6 +979,71 @@ class serendipity_event_spamblock_bee extends serendipity_event
);
}
/**
* Scramble a UTF-8 string with a simple XOR cipher in order
* to perform string obfuscation.
*
* @param string $string
* @param int $key
* @return string
*/
function xorScramble($string, $key) {
$scrambled = '';
$length = mb_strlen($string, 'UTF-8');
for ($i = 0; $i < $length; ++$i) {
$chr = mb_substr($string, $i, 1, 'UTF-8');
$ord = $this->ordUtf8($chr);
$scrambled .= $this->chrUtf8($ord ^ $key);
}
return $scrambled;
}
/**
* Multi-byte safe UTF-8 version of chr()
* Thanks to http://pastebin.com/fmiSnNin
*
* @param int $ord
* @return string
*/
function chrUtf8($ord)
{
return mb_convert_encoding(pack('n', $ord) , 'UTF-8', 'UTF-16BE');
}
/**
* Multi-byte safe UTF-8 version of ord().
* Thanks to http://pastebin.com/fmiSnNin
* Returns -1 on error.
*
* @param string $chr
* @return int
*/
function ordUtf8($chr)
{
// Return value of ord() if only single-byte
if (strlen($chr) == 1) {
return ord($chr);
}
$codePoint = ord($chr[0]);
if ($codePoint <= 0x7f) {
return $codePoint;
} else if ($codePoint < 0xc2) {
return -1;
} else if ($codePoint <= 0xdf) {
return ($codePoint & 0x1f) << 6 | (ord($chr[1]) & 0x3f);
} else if ($codePoint <= 0xef) {
return ($codePoint & 0x0f) << 12 | (ord($chr[1]) & 0x3f) << 6 | (ord($chr[2]) & 0x3f);
} else if ($codePoint <= 0xf4) {
return ($codePoint & 0x0f) << 18 | (ord($chr[1]) & 0x3f) << 12 | (ord($chr[2]) & 0x3f) << 6 | (ord($chr[3]) & 0x3f);
} else {
return -1;
}
}
/**
* Log spam to file
* @param string $message