added timeout protection against spambots into event_spamblock

This commit is contained in:
Stephan Brunker 2019-10-18 12:14:10 +02:00 committed by onli
parent 1dcde41b53
commit f9d43d20a3
4 changed files with 138 additions and 46 deletions

View File

@ -141,3 +141,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_TRACKBACKIPVALIDATION_URL_EXCLUDE', 'URLs von IP Validatierung ausnehmen');
@define('PLUGIN_EVENT_SPAMBLOCK_TRACKBACKIPVALIDATION_URL_EXCLUDE_DESC', 'URLs, die von der IP Validatierung ausgeschlossen werden sollen. ' . PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS_DESC);
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT', 'Wartezeit zum Kommentieren');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_DESC', 'Festsetzen einer Wartezeit zwischen erstem Anzeigen des Artikels und abgesandtem Kommentar');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE', 'Art der Wartezeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_FIX', 'festgesetzte Zeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_ADAPTIVE', 'abhängig von der Länge von Artikel und Kommentar');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE', 'Wert für Wartezeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE_DESC', 'festgesetzt: Wartezeit in Sekunden / abhängig: Lesen in Wörtern pro Minute und Schreiben in Zeichen pro Minute. 500 ist hier ein guter Wert selbst für schnelle Leser');
@define('PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT','Entschuldigung, aber du bist kein Mensch und solltest wenigstens versuchen den Artikel zu lesen bevor ein Kommentar verfasst wird');

View File

@ -141,3 +141,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_TRACKBACKIPVALIDATION_URL_EXCLUDE', 'URLs von IP Validatierung ausnehmen');
@define('PLUGIN_EVENT_SPAMBLOCK_TRACKBACKIPVALIDATION_URL_EXCLUDE_DESC', 'URLs, die von der IP Validatierung ausgeschlossen werden sollen. ' . PLUGIN_EVENT_SPAMBLOCK_FILTER_URLS_DESC);
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT', 'Wartezeit zum Kommentieren');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_DESC', 'Festsetzen einer Wartezeit zwischen erstem Anzeigen des Artikels und abgesandtem Kommentar');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE', 'Art der Wartezeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_FIX', 'festgesetzte Zeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_ADAPTIVE', 'abhängig von der Länge von Artikel und Kommentar');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE', 'Wert für Wartezeit');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE_DESC', 'festgesetzt: Wartezeit in Sekunden / abhängig: Lesen in Wörtern pro Minute und Schreiben in Zeichen pro Minute. 500 ist hier ein guter Wert selbst für schnelle Leser');
@define('PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT','Entschuldigung, aber du bist kein Mensch und solltest wenigstens versuchen den Artikel zu lesen bevor ein Kommentar verfasst wird');

View File

@ -147,4 +147,11 @@
@define('PLUGIN_EVENT_SPAMBLOCK_SPAM', 'Spam');
@define('PLUGIN_EVENT_SPAMBLOCK_NOT_SPAM', 'Not spam');
@define('PLUGIN_EVENT_SPAMBLOCK_LOGFILE_VALIDATE', 'Only file extensions .log and .txt are allowed');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT', 'Timeout for commenting');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_DESC', 'Activate a timeout between displaying the article and accepting a comment');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE', 'Timeout type');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_FIX', 'fixed amout of time');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_ADAPTIVE', 'depending on the lengths of comment and article');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE', 'Value for timeout');
@define('PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE_DESC', 'type fixed: timeout in seconds / type adaptive: reading in words per minute and typing in chars per minute, 500 is a good value even for fast readers');
@define('PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT','Sorry, but you are not a human or you should at least attempt to read the article before commenting');

View File

@ -25,7 +25,7 @@ class serendipity_event_spamblock extends serendipity_event
'smarty' => '2.6.7',
'php' => '4.1.0'
));
$propbag->add('version', '1.88.2');
$propbag->add('version', '1.89');
$propbag->add('event_hooks', array(
'frontend_saveComment' => true,
'external_plugin' => true,
@ -34,6 +34,7 @@ class serendipity_event_spamblock extends serendipity_event
'backend_comments_top' => true,
'backend_view_comment' => true,
'backend_sidebar_admin_appearance' => true,
'entry_display' => true
));
$propbag->add('configuration', array(
'killswitch',
@ -67,6 +68,9 @@ class serendipity_event_spamblock extends serendipity_event
'hide_email',
'checkmail',
'required_fields',
'comment_timeout',
'timeout_type',
'timeout_value',
'automagic_htaccess',
'logtype',
'logfile'));
@ -353,8 +357,6 @@ class serendipity_event_spamblock extends serendipity_event
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_LOGFILE);
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_LOGFILE_DESC);
$propbag->add('default', $serendipity['serendipityPath'] . 'spamblock-%Y-%m-%d.log');
$propbag->add('validate', '@\.(log|txt)$@imsU');
$propbag->add('validate_error', PLUGIN_EVENT_SPAMBLOCK_LOGFILE_VALIDATE);
break;
case 'logtype':
@ -450,6 +452,31 @@ class serendipity_event_spamblock extends serendipity_event
$propbag->add('default', '13');
break;
case 'comment_timeout':
$propbag->add('type', 'boolean');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TIMEOUT);
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_DESC);
$propbag->add('default', false);
break;
case 'timeout_type':
$propbag->add('type', 'radio');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE);
$propbag->add('description', '');
$propbag->add('default', 'fix');
$propbag->add('radio', array(
'value' => array('fix', 'adaptive'),
'desc' => array(PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_FIX, PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_TYPE_ADAPTIVE)
));
break;
case 'timeout_value':
$propbag->add('type', 'string');
$propbag->add('name', PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE);
$propbag->add('description', PLUGIN_EVENT_SPAMBLOCK_TIMEOUT_VALUE_DESC);
$propbag->add('default', '30');
break;
default:
return false;
}
@ -840,13 +867,16 @@ class serendipity_event_spamblock extends serendipity_event
}
$moderation_auto = $this->get_config('moderation_auto', false);
$forcemoderation = $this->get_config('forcemoderation', 60);
$forcemoderation = intval($this->get_config('forcemoderation', 60));
$forcemoderation_treat = $this->get_config('forcemoderation_treat', 'moderate');
$forcemoderationt = $this->get_config('forcemoderationt', 60);
$forcemoderationt = intval($this->get_config('forcemoderationt', 60));
$forcemoderationt_treat = $this->get_config('forcemoderationt_treat', 'moderate');
$links_moderate = $this->get_config('links_moderate', 10);
$links_reject = $this->get_config('links_reject', 20);
$links_moderate = intval($this->get_config('links_moderate', 10));
$links_reject = intval($this->get_config('links_reject', 20));
$timeout = $this->get_config('comment_timeout',false);
$timeout_type = $this->get_config('timeout_type','fix');
$timeout_value = intval($this->get_config('timeout_value',30));
if (function_exists('imagettftext') && function_exists('imagejpeg')) {
$max_char = 5;
@ -859,6 +889,19 @@ class serendipity_event_spamblock extends serendipity_event
switch($event) {
case 'entry_display':
if (!is_array($eventData)) {
return false;
}
// get a word count and save it in SESSION, because entry_display is called after saveComment
if ($timeout && $addData['extended']) {
$_SESSION['serendipity_entry_wordcount'] = str_word_count($eventData[0]['body']) + str_word_count($eventData[0]['extended']);
} else {
if (isset($_SESSION['serendipity_entry_wordcount'])) unset( $_SESSION['serendipity_entry_wordcount']);
}
break;
case 'fetchcomments':
if (is_array($eventData) && !$_SESSION['serendipityAuthedUser'] && serendipity_db_bool($this->get_config('hide_email', 'false'))) {
// Will force emails to be not displayed in comments and RSS feed for comments. Will not apply to logged in admins (so not in the backend as well)
@ -891,6 +934,7 @@ class serendipity_event_spamblock extends serendipity_event
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_CSRF_REASON, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_CSRF_REASON;
return false;
}
}
@ -931,7 +975,7 @@ class serendipity_event_spamblock extends serendipity_event
if (!is_array($auth)) {
// Filter authors names, Filter URL, Filter Content, Filter Emails, Check for maximum number of links before rejecting
// moderate false
if(false === $this->wordfilter($logfile, $eventData, $wordmatch, $addData, true)) {
if(false === $this->wordfilter($logfile, $eventData, $wordmatch, $addData)) {
// already there #$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_FILTER_WORDS, $addData);
// already there #$eventData = array('allow_comments' => false);
// already there #$serendipity['messagestack']['emails'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY;
@ -947,7 +991,6 @@ class serendipity_event_spamblock extends serendipity_event
$eventData['status'] = 'confirm1';
$serendipity['csuccess'] = 'moderate';
$serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_CHECKMAIL_VERIFICATION_MAIL;
return false;
}
} else {
// User is allowed to post message, bypassing other checks as if he were logged in.
@ -990,7 +1033,9 @@ class serendipity_event_spamblock extends serendipity_event
// Check if sender ip is matching trackback/pingback ip (ip validation)
$trackback_ipvalidation_option = $this->get_config('trackback_ipvalidation','moderate');
if (($addData['type'] == 'TRACKBACK' || $addData['type'] == 'PINGBACK') && $trackback_ipvalidation_option != 'no') {
$this->IsHardcoreSpammer();
// $this->IsHardcoreSpammer(); this will block every pingback address ?!
// check for urls excluded from the ip check
$exclude_urls = explode(';',$this->get_config('trackback_ipvalidation_url_exclude', $this->get_default_exclude_urls()));
$found_exclude_url = false;
foreach ($exclude_urls as $exclude_url) {
@ -1042,13 +1087,13 @@ class serendipity_event_spamblock extends serendipity_event
if (!empty($akismet_apikey) && ($akismet == 'moderate' || $akismet == 'reject') && !isset($addData['skip_akismet'])) {
$spam = $this->getBlacklist('akismet.com', $akismet_apikey, $eventData, $addData);
if ($spam['is_spam'] !== false) {
$this->IsHardcoreSpammer();
if ($akismet == 'moderate') {
$this->log($logfile, $eventData['id'], 'MODERATE', PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $addData);
$eventData['moderate_comments'] = true;
$serendipity['csuccess'] = 'moderate';
$serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY . ' (Akismet)';
} else {
$this->IsHardcoreSpammer();
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_AKISMET_SPAMLIST . ': ' . $spam['message'], $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY;
@ -1100,15 +1145,6 @@ class serendipity_event_spamblock extends serendipity_event
return false;
}
// Check for maximum number of links before rejecting
$link_count = substr_count(strtolower($addData['comment']), 'http://');
if ($links_reject > 0 && $link_count > $links_reject) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_REJECT, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY;
return false;
}
// Captcha checking
if ($show_captcha && $addData['type'] == 'NORMAL') {
if (!isset($_SESSION['spamblock']['captcha']) || !isset($serendipity['POST']['captcha']) || strtolower($serendipity['POST']['captcha']) != strtolower($_SESSION['spamblock']['captcha'])) {
@ -1117,17 +1153,15 @@ class serendipity_event_spamblock extends serendipity_event
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_CAPTCHAS;
return false;
} else {
// DEBUG
// $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha passed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData);
// $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha passed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData);
}
} else {
// DEBUG
// $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha not needed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData);
// $this->log($logfile, $eventData['id'], 'REJECTED', 'Captcha not needed: ' . $serendipity['POST']['captcha'] . ' / ' . $_SESSION['spamblock']['captcha'] . ' // Source: ' . $_SERVER['REQUEST_URI'], $addData);
}
// Check for forced comment moderation (X days)
if ($addData['type'] == 'NORMAL' && $moderation_auto == true && (
( $forcemoderation == 0 ) ||
if ($addData['type'] == 'NORMAL' && $moderation_auto == true && (
( $forcemoderation == 0 ) ||
( $forcemoderation > 0 && $eventData['timestamp'] < (time() - ($forcemoderation * 60 * 60 * 24)) ) ) ) {
$this->log($logfile, $eventData['id'], $forcemoderation_treat, PLUGIN_EVENT_SPAMBLOCK_REASON_FORCEMODERATION, $addData);
if ($forcemoderation_treat == 'reject') {
@ -1155,14 +1189,6 @@ class serendipity_event_spamblock extends serendipity_event
}
}
// Check for maximum number of links before forcing moderation
if ($links_moderate > 0 && $link_count > $links_moderate) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE, $addData);
$eventData['moderate_comments'] = true;
$serendipity['csuccess'] = 'moderate';
$serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE;
}
// Check for identical comments. We allow to bypass trackbacks from our server to our own blog.
if ( $this->get_config('bodyclone', false) === true && $_SERVER['REMOTE_ADDR'] != $_SERVER['SERVER_ADDR'] && $addData['type'] == 'NORMAL') {
$query = "SELECT count(id) AS counter FROM {$serendipity['dbPrefix']}comments WHERE type = '" . $addData['type'] . "' AND body = '" . serendipity_db_escape_string($addData['comment']) . "'";
@ -1200,6 +1226,7 @@ class serendipity_event_spamblock extends serendipity_event
// Check invalid email
if ($addData['type'] == 'NORMAL' && serendipity_db_bool($this->get_config('checkmail', 'false'))) {
if (!empty($addData['email']) && strstr($addData['email'], '@') === false) {
// todo: only an '@' is no proper check of an email ...
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_CHECKMAIL, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_CHECKMAIL;
@ -1207,6 +1234,39 @@ class serendipity_event_spamblock extends serendipity_event
}
}
// Check for comment timeout
if ($timeout) {
// time passed in seconds since displaying the article
$usedtime = time() - $_SESSION['serendipity_comment_timeout'];
switch ($timeout_type) {
case 'fix':
if ($usedtime < $timeout_value) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT;
return false;
}
break;
case 'adaptive':
// reading time for (value) words per minute
$readtime = $_SESSION['serendipity_entry_wordcount'] * 60 / $timeout_value;
// writing time for (value) chars per minute
$writetime = strlen($addData['comment']) * 60 / $timeout_value;
if ($usedtime < $readtime + $writetime) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_REASON_TIMEOUT;
return false;
}
break;
}
}
if ($eventData['moderate_comments'] == true) {
return false;
}
@ -1226,6 +1286,9 @@ class serendipity_event_spamblock extends serendipity_event
echo serendipity_setFormToken('form');
}
if ($timeout) $_SESSION['serendipity_comment_timeout'] = time();
// Check whether to allow comments from registered authors
if (serendipity_userLoggedIn() && $this->inGroup()) {
return true;
@ -1404,7 +1467,7 @@ class serendipity_event_spamblock extends serendipity_event
/**
* wordfilter, email and additional link check moved to this function, to allow comment user to opt-in (verify_once), but reject all truly spam comments before.
**/
function wordfilter($logfile, &$eventData, $wordmatch, $addData, $ftc = false)
function wordfilter($logfile, &$eventData, $wordmatch, $addData)
{
global $serendipity;
@ -1511,17 +1574,23 @@ class serendipity_event_spamblock extends serendipity_event
}
} // Content filtering end
if($ftc) {
// Check for maximum number of links before rejecting
$link_count = substr_count(strtolower($addData['comment']), 'http://');
$links_reject = $this->get_config('links_reject', 20);
if ($links_reject > 0 && $link_count > $links_reject) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_REJECT, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY;
return false;
}
// Check for maximum number of links in comment body to reject
$link_count = substr_count(strtolower($addData['comment']), 'http://');
if ($links_reject > 0 && $link_count > $links_reject) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_REJECT, $addData);
$eventData = array('allow_comments' => false);
$serendipity['messagestack']['comments'][] = PLUGIN_EVENT_SPAMBLOCK_ERROR_BODY;
return false;
}
// Check for maximum number of links before forcing moderation
if ($links_moderate > 0 && $link_count > $links_moderate) {
$this->log($logfile, $eventData['id'], 'REJECTED', PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE, $addData);
$eventData['moderate_comments'] = true;
$serendipity['csuccess'] = 'moderate';
$serendipity['moderate_reason'] = PLUGIN_EVENT_SPAMBLOCK_REASON_LINKS_MODERATE;
}
} // function wordfilter end