Implemented optional RegExp matching for questions, reduced number of get_config() calls and fixed a JavaScript error that occured when fetching answer via Ajax

This commit is contained in:
Janek Bevendorff 2012-08-12 16:05:43 +02:00 committed by Janek Bevendorff
parent a1caf4519a
commit 8b8b76642a
6 changed files with 142 additions and 64 deletions

7
.directory Normal file
View file

@ -0,0 +1,7 @@
[Dolphin]
HeaderColumnWidths=307,70,119,133,56,81
PreviewsShown=true
Timestamp=2012,8,11,16,10,32
Version=3
ViewMode=1
VisibleRoles=Details_text,Details_size,Details_date,Details_type,Details_owner,Details_permissions,CustomizedDetails

View file

@ -56,7 +56,7 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWERS_DESC', 'Dieses Feld enthält die korrekten Antworten auf die oben angegebenen Fragen. Gib pro Zeile eine Antwort an in derselben Reihenfolge, die auch die Fragen haben. Fragen, für die es keine Antworten gibt, werden ignoriert. Groß- und Kleinschreibung spielt keine Rolle (d.h. "Antwort" ist dasselbe wie "antwort".');
@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.');
@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_CAPTCHA_0', 'Null');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'Eins');

View file

@ -56,7 +56,7 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWERS_DESC', 'Dieses Feld enthält die korrekten Antworten auf die oben angegebenen Fragen. Gib pro Zeile eine Antwort an in derselben Reihenfolge, die auch die Fragen haben. Fragen, für die es keine Antworten gibt, werden ignoriert. Groß- und Kleinschreibung spielt keine Rolle (d.h. "Antwort" ist dasselbe wie "antwort".');
@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.');
@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_CAPTCHA_0', 'Null');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'Eins');

View file

@ -58,7 +58,7 @@
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_ADV_ANSWERS_DESC', 'This field contains the correct answers for the questions specified above. Write down one answer per line in the same order as the corresponding questions. Questions that don\'t have a valid answer will be ignored. All answers are case-insensitive (i.e. "Answer" is the same as "answer").');
@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.');
@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_CAPTCHA_0', 'zero');
@define('PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_1', 'one');

View file

@ -80,7 +80,7 @@
if (typeof answer != 'string' || 'ERROR' != answer.toUpperCase()) {
inputCaptcha.value = answer;
this.hideBeeElement();
that.hideBeeElement();
}
}
}

View file

@ -23,7 +23,22 @@ require_once dirname(__FILE__) . '/json/json.php4.include.php';
class serendipity_event_spamblock_bee extends serendipity_event
{
var $title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE;
var $title = PLUGIN_EVENT_SPAMBLOCK_BEE_TITLE;
var $useHoneyPot = true;
var $hiddenCaptchaHandle = null;
var $answerRetrievalMethod = null;
var $captchaAnswer = array();
var $captchaQuestionType = null;
var $useRegularExpressions = false;
function serendipity_event_spamblock_bee() {
$this->answerRetrievalMethod = $this->get_config('answer_retrieval_method', 'default');
$this->captchaQuestionType = $this->get_config('question_type', 'math');
$this->useHoneyPot = $this->get_config('do_honeypot', true);
$this->hiddenCaptchaHandle = $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE);
$this->useRegularExpressions = $this->get_config('use_regexp', false);
}
function introspect(&$propbag)
{
@ -44,8 +59,8 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('event_hooks', array(
'frontend_comment' => true,
'frontend_saveComment' => true,
'frontend_footer' => true,
'css' => true,
'frontend_footer' => true,
'css' => true,
'external_plugin' => true,
));
$propbag->add('groups', array('ANTISPAM'));
@ -60,7 +75,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$propbag->add('configuration', $configuration );
$propbag->add('config_groups', array(
PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SECTION_LOGGING => array(
'spamlogtype', 'spamlogfile', 'plugin_path'
'spamlogtype', 'spamlogfile', 'plugin_path'
),
PLUGIN_EVENT_SPAMBLOCK_BEE_CONFIG_SECTION_ADVANCED => array(
'advanced_cc_desc', 'answer_retrieval_method', 'question_type', 'questions', 'answers', 'use_regexp'
@ -80,7 +95,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
$rejectType = array(
PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_OFF,
PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_MODERATE,
PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_REJECT,
PLUGIN_EVENT_SPAMBLOCK_SWTCH_REJECT => PLUGIN_EVENT_SPAMBLOCK_BEE_RESULT_REJECT,
);
$retrievalMethod = array(
@ -227,19 +242,18 @@ class serendipity_event_spamblock_bee extends serendipity_event
echo file_get_contents(dirname(__FILE__). '/spamblockbee.png');
break;
case 'spamblockbeecaptcha':
echo $this->produceCaptchaAnswer();
echo $this->produceCaptchaAnswerJson();
break;
}
break;
case 'frontend_saveComment':
// Check only, if noone else denied it before
if (!is_array ( $eventData ) || serendipity_db_bool ( $eventData ['allow_comments'] )) {
$result = $this->checkComment($eventData, $addData);
return $result;
}
// Check only, if no one else denied it before
if (!is_array ( $eventData ) || serendipity_db_bool ( $eventData ['allow_comments'] )) {
return $this->checkComment($eventData, $addData);
}
return true;
break;
break;
case 'frontend_comment':
$this->printCommentEditExtras($eventData, $addData);
break;
@ -261,8 +275,10 @@ class serendipity_event_spamblock_bee extends serendipity_event
return false;
}
}
function install() {
}
function cleanup() {
}
@ -271,35 +287,57 @@ class serendipity_event_spamblock_bee extends serendipity_event
if ("NORMAL" == $addData['type']) { // only supported for normal comments
// Check for honeypot:
$do_honepot = serendipity_db_bool($this->get_config('do_honeypot',true));
if ($do_honepot && (!empty($serendipity['POST']['phone']) || $serendipity['POST']['phone']=='0') ) {
// Check for honey pot:
if ($this->useHoneyPot && (!empty($serendipity['POST']['phone']) || $serendipity['POST']['phone']=='0') ) {
$this->spamlog($eventData['id'], 'REJECTED', "BEE Honeypot [" . $serendipity['POST']['phone'] . "]", $addData);
$eventData = array('allow_comments' => false);
return false;
}
// Check hidden captcha
$spamHandle = $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE);
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $spamHandle) {
$answer = trim(strtolower($serendipity['POST']['beecaptcha']));
$correctAnswer = strtolower($_SESSION['spamblockbee']['captcha']);
// Check hidden Captcha
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$answer = trim(strtolower($serendipity['POST']['beecaptcha']));
$correctAnswer = $this->getCaptchaAnswer();
$correctAnswer['answer'] = strtolower($correctAnswer['answer']);
$isCorrect = false;
$correct = ($answer == $correctAnswer);
// also allow numbers as words
if (!$correct && $this->get_config('question_type', 'math') == 'math') {
$number = $this->generateNumberString($correctAnswer);
$correct = ($answer == $number && $number != 'ERROR');
// If provided answer is longer than 1000 characters and RegExp matching is on,
// reject comment for security reasons (minimize risk of ReDoS)
if ($this->useRegularExpressions && strlen($answer) > 1000) {
return false;
}
if (!$correct) {
$test = $this->generateNumberString($answer);
if (strtolower($correct) != strtolower($test)) {
$this->processComment($spamHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_HCAPTCHA, "BEE HiddenCaptcha [ $correct != $answer ]");
return false;
if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) {
// Sanitize regular expression and remove answer part
$pattern = preg_replace('/^\s*\/(.*)\/\s*[imsxeADSUXJu]*\s*$/s', '$1', $correctAnswer['pattern']);
$pattern = addcslashes($pattern, '\\');
// Try to match pattern with given answer
$match = @preg_match('/' . $pattern . '/si', $answer);
// If pattern contains errors, fall back to basic string comparison
if ($match === false) {
$this->useRegularExpressions = false;
} else {
$isCorrect = ($match === 1);
}
}
if ($this->captchaQuestionType != 'custom' || !$this->useRegularExpressions) {
$isCorrect = ($answer == $correctAnswer['answer']);
}
// Also allow numbers as words
if (!$isCorrect && $this->captchaQuestionType == 'math') {
$number = $this->generateNumberString($correctAnswer['answer']);
$isCorrect = ($answer == $number && $number != 'ERROR');
}
if (!$isCorrect) {
$this->processComment($this->hiddenCaptchaHandle, $eventData, $addData, PLUGIN_EVENT_SPAMBLOCK_BEE_ERROR_HCAPTCHA, "BEE HiddenCaptcha [ $correct != $answer ]");
}
return $isCorrect;
}
}
@ -396,29 +434,29 @@ class serendipity_event_spamblock_bee extends serendipity_event
$this->log(print_r($serendipity['messagestack'], true));
}
function produceCaptchaAnswer() {
$answer = isset($_SESSION['spamblockbee']['captcha']) ? $_SESSION['spamblockbee']['captcha'] : null;
if (null === $answer) {
$answer="ERROR";
function produceCaptchaAnswerJson() {
$answer = $this->getCaptchaAnswer();
if (null === $answer['answer']) {
$answer='ERROR';
}
return json_encode(array("answer" => $answer));
return json_encode(array('answer' => $answer['answer']));
}
function printJsExtras() {
$method = $this->get_config('answer_retrieval_method', 'default');
if ($method == 'smarty') {
if ($this->answerRetrievalMethod == 'smarty') {
return;
}
global $serendipity;
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE)) {
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$path = $this->path = $this->get_config('plugin_path', $serendipity['serendipityHTTPPath'] . 'plugins/serendipity_event_spamblock_bee/');
$answer = $_SESSION['spamblockbee']['captcha'];
$answer = $this->getCaptchaAnswer();
$answer = $answer['answer'];
echo '<script> var spamBeeData = {';
if ($method == 'json') {
if ($this->answerRetrievalMethod == 'json') {
echo "'url': '" . $serendipity['baseURL'] . "index.php/plugin/spamblockbeecaptcha', " .
"'method': 'json'";
} else {
@ -438,7 +476,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
if (isset($eventData['GET']['action']) && $eventData['GET']['action']=='admin') return;
// Honeypot
if (serendipity_db_bool($this->get_config('do_honeypot',true))) {
if (serendipity_db_bool($this->useHoneyPot)) {
echo '<div id="serendipity_comment_phone" class="serendipity_commentDirection comment_phone_input" >' . "\n";
echo '<label for="serendipity_commentform_phone">Phone*</label>' . "\n";
echo '<input class="comment_phone_input" type="text" id="serendipity_commentform_phone" name="serendipity[phone]" value="" placeholder="' . PLUGIN_EVENT_SPAMBLOCK_BEE_WARN_HONEPOT . '"/>' . "\n";
@ -446,8 +484,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
}
// Captcha
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF !=
$this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE)) {
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
$question = $this->generateCaptchaQuestion();
echo '<div id="serendipity_comment_beecaptcha" class="form_field">' . "\n";
@ -461,7 +498,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
global $serendipity;
// Hide and reveal classes by @yellowled used be the RSS chooser:
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->get_config('do_hiddencaptcha', PLUGIN_EVENT_SPAMBLOCK_SWTCH_MODERATE)) {
if (PLUGIN_EVENT_SPAMBLOCK_SWTCH_OFF != $this->hiddenCaptchaHandle) {
?>
.spambeehidden {
border: 0;
@ -479,9 +516,9 @@ class serendipity_event_spamblock_bee extends serendipity_event
if (!(strpos($eventData, '.comment_phone_input'))) {
?>
.comment_phone_input {
max-width: 100%;
display:none;
visibility:hidden;
max-width: 100%;
display:none;
visibility:hidden;
}
<?php
}
@ -497,23 +534,21 @@ class serendipity_event_spamblock_bee extends serendipity_event
}
function generateCaptchaQuestion() {
$questionType = $this->get_config('question_type', 'math');
if ($questionType == 'custom') {
if ($this->captchaQuestionType == 'custom') {
$question = $this->selectRandomCustomCaptchaQuestion();
if (null === $question) {
// no valid question could be selected, fall back to math questions
$questionType = 'math';
$this->set_config('question_type', 'math');
} else {
$_SESSION['spamblockbee']['captcha'] = $question['answer'];
$this->setCaptchaAnswer($question['answer']);
return $question['question'];
}
}
if ($questionType == 'math') {
$captchaData = $this->generateCaptchaMathProblem();
$_SESSION['spamblockbee']['captcha'] = $captchaData['answer'];
if ($this->captchaQuestionType == 'math') {
$captchaData = $this->generateCaptchaMathProblem();
$this->setCaptchaAnswer($captchaData['answer']);
$method = PLUGIN_EVENT_SPAMBLOCK_BEE_CAPTCHA_PLUS;
if ($captchaData['operator'] == '-') {
@ -526,6 +561,42 @@ class serendipity_event_spamblock_bee extends serendipity_event
}
}
function getCaptchaAnswer() {
if (!isset($this->captchaAnswer['answer']) && isset($_SESSION['spamblockbee']['captcha'])) {
$this->captchaAnswer = $_SESSION['spamblockbee']['captcha'];
}
// If for some reason RegExp matching is on, but no pattern is present,
// turn of RegExp matching
if ($this->useRegularExpressions && !isset($this->captchaAnswer['pattern'])) {
$this->useRegularExpressions = false;
}
return $this->captchaAnswer;
}
function setCaptchaAnswer($answer) {
$answer = array('answer' => $answer);
// Split answer into array if RegExp matching is on
if ($this->captchaQuestionType == 'custom' && $this->useRegularExpressions) {
$delimiterIndex = strrpos($answer['answer'], ':');
if ($delimiterIndex !== false) {
$answer = array(
'pattern' => substr($answer['answer'], 0, $delimiterIndex),
'answer' => substr($answer['answer'], $delimiterIndex + 1)
);
} else {
// Answer contains either no pattern or no answer part, fall back to string matching
$this->useRegularExpressions = false;
}
}
$this->captchaAnswer = $answer;
$_SESSION['spamblockbee']['captcha'] = $this->captchaAnswer;
}
function generateCaptchaMathProblem() {
$result = array();
@ -608,7 +679,7 @@ class serendipity_event_spamblock_bee extends serendipity_event
fclose($fp);
}
function spamlog($id, $switch, $reason, $addData) {
function spamlog($id, $switch, $reason, $addData) {
global $serendipity;
$method = $this->get_config('spamlogtype', 'none');
@ -619,9 +690,9 @@ class serendipity_event_spamblock_bee extends serendipity_event
if (empty($logfile)) {
return;
}
if (strpos($logfile, '%') !== false) {
$logfile = strftime($logfile);
}
if (strpos($logfile, '%') !== false) {
$logfile = strftime($logfile);
}
$fp = @fopen($logfile, 'a+');
if (!is_resource($fp)) {
@ -672,4 +743,4 @@ class serendipity_event_spamblock_bee extends serendipity_event
break;
}
}
}
}