I really don't think this is that hard, someone just needs to figure out how to do it and I am sure it would be immensely popular. While honeypot does work excellently, it still does not prevent the "tons" a requests that a bad bot hits a website with. On my site, a relatively popular pet related site I literally get thousands of banned posts a day, and multiple ones per minute/second. Check the screen shot:(might only show as an attachment)

Bad Bot Log

Anyway this is a slow day, in either case while I am super happy that honeypot prevents a lot of garbage posts, it would be nice if bad bots could just be IP banned after their 1st, 2nd attempt etc. Let's face it, if someone or something is filling in a field that says "do not fill in you will be banned" then its probably a bot.

Looking through the code, I found the spot where honeypot checks the form for a submission or if the time is below the thresh hold:

function _honeypot_honeypot_validate($element, &$form_state) {
  // Get the honeypot field value.
  $honeypot_value = $element['#value'];

  // Make sure it's empty.
  if (!empty($honeypot_value)) {
    _honeypot_log($form_state['values']['form_id'], 'honeypot');
    form_set_error('', t('There was a problem with your form submission. Please refresh the page and try again.'));
  }
}

* Validate honeypot's time restriction field.
 */
function _honeypot_time_restriction_validate($form, &$form_state) {
  // Get the time value.
  $honeypot_time = $form_state['values']['honeypot_time'];

  // Get the honeypot_time_limit.
  $time_limit = honeypot_get_time_limit($form_state['values']);

  // Make sure current time - (time_limit + form time value) is greater than 0.
  // If not, throw an error.
  if (time() < ($honeypot_time + $time_limit)) {
    _honeypot_log($form_state['values']['form_id'], 'honeypot_time');
    $time_limit = honeypot_get_time_limit($form_state['values']);
    $form_state['values']['honeypot_time'] = time();
    form_set_error('', t('There was a problem with your form submission. Please wait @limit seconds and try again.', array('@limit' => $time_limit)));
  }
}

/**
 * Log blocked form submissions.
 *
 * @param $form_id
 *   Form ID for the form on which submission was blocked.
 * @param $type
 *   String indicating the reason the submission was blocked. Allowed values:
 *     - 'honeypot'
 *     - 'honeypot_time'
 */
function _honeypot_log($form_id, $type) {
  honeypot_log_failure();
  if (variable_get('honeypot_log', 0)) {
    $variables = array(
        '%form'  => $form_id,
        '@cause' => ($type == 'honeypot') ? t('submission of a value in the honeypot field') : t('submission of the form in less than minimum required time'),
    );
    watchdog('honeypot', 'Blocked submission of %form due to @cause.', $variables);
  }
  return;
}

It would be nice if we could set a variable in the admin section similar to troll:

	function troll_blacklist_punishment_form_submit($form, &$form_state) {
  variable_set('troll_blacklist_mod_requests', $form_state['values']['mod_requests']);
  variable_set('troll_blacklist_stutter', $form_state['values']['stutter']);
<strong>  variable_set('troll_blacklist_alt_page', $form_state['values']['alt_page']);
  variable_set('troll_blacklist_alt_url', $form_state['values']['alt_url']);</strong>
  drupal_set_message(t('The settings have been updated.'));

Then call that variable when Honeypot checks to form state to see if it has been filled in and redirect them to a page the automatically blacklists them, similar to this function in troll:

function troll_boot() {
  $ip = ip_address();
  if (troll_is_blacklisted()) {
    $alt_page = variable_get('troll_blacklist_alt_page', 0);
    if ($alt_page) {
      switch ($alt_page) {
        case 'blank':
          watchdog('troll', 'Alternate blank page displayed to @ip', array('@ip' => $ip), WATCHDOG_INFO);
          exit;
          break;
        case '404':
          // drupal_not_found() can make a mess in admin logs with watchdog()
          drupal_set_header('HTTP/1.0 404 Not Found');
          drupal_set_title(t('Page not found'));
          watchdog('troll', 'Alternate 404 page displayed to @ip', array('@ip' => $ip), WATCHDOG_INFO);
          print theme('page', '');
          exit;
          break;
        case 'redirect':
          // The default value of troll_blacklist_alt_url should not be an
          // empty string because then we redirect to ourselves; so use 127.0.0.1.
          $url = variable_get('troll_blacklist_alt_url', 'http://127.0.0.1');
          watchdog('troll', '@ip redirected to @url', array('@ip' => $ip, '@url' => $url), WATCHDOG_INFO);
          header('Location: '. $url);
          exit;
          break;
      }
    }

Granted the variable is already set in this case.. but we could add the redirect at the bottom of the Honeypot check form state to redirect them to a page like this:

goodbye_byatch.php
<?php
$filename = '/path/to/.htaccess'; // CHANGE IF NECESSARY
if ($fp = fopen($filename, 'a')) {
      if (flock($fp, LOCK_EX)) {
            $remote_addr = $_SERVER['REMOTE_ADDR'];
            fwrite($fp, "deny from $remote_addr\n");

      }

       flock($fp, LOCK_UN);
      fclose($fp);
}

header('HTTP/1.1 403 Forbidden');
echo "Forbidden!"; // Add any other HTML here
exit();

// EOF

 ?>

Which would automatically write their IP into the .htaccess and ban them. Thus saving my poor server, hundred, thousands, of failed form requests, and saving me hours of going through and manually banning the ips..

I'm not a php guy but maybe something along the lines of

function custom_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'form_set_error':
        $form['#redirect'] = 'goodbye_byatch.php';
      break;
  }
}
 

Again I'm not a php guy so I may be way off with the above script idea.. but a simple redirect based upon whether they failed validation does not seem that difficult.
Ideas?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

zeroyon’s picture

Issue summary: View changes

formatting

zeroyon’s picture

In the time that it took me to write the above post:

honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:53 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:53 comment_form post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of comment_form due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of comment_form due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 comment_form post blocked by CAPTCHA ... Anonymous
user 12/05/2012 - 08:52 Login attempt failed for tuttDyedNum. Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:52 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:52 user_register post blocked by CAPTCHA ... Anonymous
warning page not found 12/05/2012 - 08:46 user/spam/denied Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:46 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:46 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:45 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:45 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:45 user_register post blocked by CAPTCHA ... Anonymous
warning page not found 12/05/2012 - 08:45 dog_clubs_by_breed Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:45 user_register post blocked by CAPTCHA ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
honeypot 12/05/2012 - 08:45 Blocked submission of user_register due to ... Anonymous
CAPTCHA 12/05/2012 - 08:45 user_register post blocked by CAPTCHA ... Anonymous

My poor server, it would be so cool, if after the first attempt their IP was just banned...

zeroyon’s picture

A temporary external solution that seems to work... but I'm sure this could be included in the module...

I created a file called byebye.php in it I put

<?php
$filename = '.htaccess'; // CHANGE IF NECESSARY
if ($fp = fopen($filename, 'a')) {
      if (flock($fp, LOCK_EX)) {
            $remote_addr = $_SERVER['REMOTE_ADDR'];
            fwrite($fp, "deny from $remote_addr\n");

      }

       flock($fp, LOCK_UN);
      fclose($fp);
}

header('HTTP/1.1 403 Forbidden');
echo "Forbidden!"; // Add any other HTML here
exit();

// EOF

?>

I went into honeypot.module and added the following line around 198 and 220

/**
 * Validate honeypot field.
 */
function _honeypot_honeypot_validate($element, &$form_state) {
  // Get the honeypot field value.
  $honeypot_value = $element['#value'];

  // Make sure it's empty.
  if (!empty($honeypot_value)) {
    _honeypot_log($form_state['values']['form_id'], 'honeypot');
    form_set_error('', t('There was a problem with your form submission. Please refresh the page and try again.'));
++  //redirect bad bot to page that blocks IP  
++    drupal_goto('byebye.php');
   
  }
}

/**
 * Validate honeypot's time restriction field.
 */
function _honeypot_time_restriction_validate($form, &$form_state) {
  // Get the time value.
  $honeypot_time = $form_state['values']['honeypot_time'];

  // Get the honeypot_time_limit.
  $time_limit = honeypot_get_time_limit($form_state['values']);

  // Make sure current time - (time_limit + form time value) is greater than 0.
  // If not, throw an error.
  if (time() < ($honeypot_time + $time_limit)) {
    _honeypot_log($form_state['values']['form_id'], 'honeypot_time');
    $time_limit = honeypot_get_time_limit($form_state['values']);
    $form_state['values']['honeypot_time'] = time();
    form_set_error('', t('There was a problem with your form submission. Please wait @limit seconds and try again.', array('@limit' => $time_limit)));
++    //redirect bad bot to page that blocks IP  
++   drupal_goto('byebye.php');
	
  }
}

I know its a cheap and unsophisticated hack (that to be honest I am not sure won't mess something else up) but it seems to be working as it is adding the following at the bottom of my .htaccess

deny from 120.28.225.250
deny from 173.83.251.72
deny from 173.246.46.224
deny from 195.14.0.201

zeroyon’s picture

Seems to be working as it does not even give a spammer a chance to figure out what the hell happened.. One strike on the honeypot field and their out and banned from the server. "403 Denied, You do not have access to:"

geerlingguy’s picture

I know its a cheap and unsophisticated hack (that to be honest I am not sure won't mess something else up)

Haha, often those are the best :)

Anyways, I know some sites that simply have a custom module that looks at every blocked submission (you can hook into Honeypot's different actions—see the .api.php file with the module for more), and then bans people using Drupal's built in IP blocking system.

I might just need to work on making it easier to use this module along with Rules, Troll, or other modules to hook in and ban a user based on whatever rules you want, without having to code anything.

zeroyon’s picture

Anyways, I know some sites that simply have a custom module that looks at every blocked submission (you can hook into Honeypot's different actions—see the .api.php file with the module for more), and then bans people using Drupal's built in IP blocking system.

I was thinking about that, checking the recent log entries and for example. Upon a Captcha failure have it look to see if %ip had failed x times before.. I understand real users will occasionally flub the Captcha test but if it done x times in x seconds then they are probably a spammer, and if they fail x times then they are probably to dumb to use the site even if they are a real user.. But I have no idea how I would code that in or add it..

Great idea though..

zeroyon’s picture

Here is some code that I found, that basically gives a report of failed Captcha attempts. I am sure you (being a php guy) could add in or apply the logic so as it finds failed Honeypot attempts as well.. Then add a function that adds the "deny from $ipaddress" into the .httaccess. or adds a threshold limit for captcha and honeypot that any amount of attempts above x or attempts above x in y time. Executes the function and bans them..

this of course could morph into a completely separate module all together but a useful one I am sure as it currently does not exist..

If there is anyone good with php, they could probably change the line that turns the ip address in to a linkable field and sends you to what's my ip into a function that just adds that IP to the .htaccess file..

/* ==============================================================================
* System        : Drupal (Tested on Drupal 6)
* Author        : Grant at mmt.org
* Date Written  : December 2010
* Description   : This PHP code snippet produces several reports on how often
*                 your Drupal site gets attacked by spammers, where the attacks
*                 come from and which pages they are hitting most. It is designed
*                 to work as a stand-alone piece of code, but you could easily
*                 tweak it, style it or what have you.
* How to use it : Cut and paste this code into a page or other node, preferably
*                 one that is secure or even unpublished. You may want to remove
*                 a few HTML BR tags and the like if you want to use CSS styling.
* Dependencies  : Since this code pulls recent CAPTCHA stats from the logs it is
*                 dependent on your dblog being active and having enough entries
*                 and on you actually using CAPTCHA. It also makes use of jQuery,
*                 which is built in to Drupal.
* Disclaimers   : This code is designed to just be dropped into a page as a
*                 snippet and as such does not do everything "the Drupal way."
* Examples      : You can see examples of the reports from here:
*                 Overview and Summary: bit.ly/fnbs95
*                 Report by date: bit.ly/hHp1ng
*                 IP Address report: bit.ly/fckR8b
* ==============================================================================
*/

/**
* ------------------------------------------------------------------------------
* Fist show the user a standard Drupal error message if the DBLog module is
* not enabled.
* ------------------------------------------------------------------------------
*/
if (! module_exists('dblog')) { // If the DBLog module is not enabled
  echo
  drupal_set_message(
    t('The DBLog module is not active and it is required for this snippet to function correctly. ' .
      'Go to <a href="@module-page">the module list</a> and enable "Database logging" for this to report to work correctly',
      array('@module-page' => url('admin/build/modules'))),
    'error', FALSE);
}


/**
* ------------------------------------------------------------------------------
* Pick up all logged Captcha blocks, latest first. If you'd like to search on
* more than just Mollom and CAPTCHA then simply add another entry to the IN
* clause in the SQL SELECT statement.
* ------------------------------------------------------------------------------
*/
$result  = db_query("
  SELECT variables, location, timestamp, hostname
    FROM {watchdog}
   WHERE type IN ('CAPTCHA', 'mollom')
   ORDER BY timestamp DESC" );

/**
* ------------------------------------------------------------------------------
* Go through the log entries and accumulate totals by date, and within date, by
* form/page. Also accumulate counters by hostname for a second report.
* ------------------------------------------------------------------------------
*/
// Define some work variables.
$firstTimestamp  = $lastTimestamp  = $overallCounter  = 0;
$currentDate     = '';
$dateTotals      = $hostTotals  = $dailyOffenders  = $overallTotals  = array();

// Loop through the CAPTCHA log entries found.
while ($captchaBlock = db_fetch_array($result)) {
  if ($lastTimestamp == 0) { // If this is the first row for a given date
    $lastTimestamp  = $captchaBlock['timestamp'];
  }

  // Save date and counter for later use
  $startingFrom    = format_interval(time() - $captchaBlock['timestamp']); // Display version of earliest date
  $firstTimestamp  = $captchaBlock['timestamp']; // Raw version of earliest date
  $overallCounter++;

  // Set up the base url, without the host, i.e. abc.com/xyz becomes /xyz
  $afterHost   = strpos($captchaBlock['location'], $_SERVER['HTTP_HOST']) + strlen($_SERVER['HTTP_HOST']);
  $pageSubUrl  = trim(substr($captchaBlock['location'], $afterHost));

  // Get the form name.
  $details  = unserialize($captchaBlock['variables']); // Extract the variable data

  // Set the identifier as either the form name or a page.
  if (isset($details['%form_id'])) { // If we have a form name
    // Set up a user-readable title. In most cases we use the form id, but in some cases
    // we use the URL, because the name is likely to be more meaningful.
    $title  = $details['%form_id']; // Default
    if (substr($details['%form_id'], 0, 7) == 'webform') { // If the 1st 7 characters of the form id are 'webform'
      if (substr($pageSubUrl, 1, 4) != 'node') { // If this is not a node URL
        $title  = substr($pageSubUrl, 1);
      }
    }
  
    // Turn the form name/alias into a user-readable title.
      $formName  = ucwords(str_replace(array('_', '-', '/'), ' ', $title));
  
    // If this is a webform then make it a clickable link. We don't do this with everything
    // as it is usually TMI, e.g. with comments being all over a site, you'd have to have a
    // line for each comment form. Also, making the user register page clickable is pointless
    // as that page is not viewable while logged in. If you want a clickable link for
    // everything, simply remove the if statement below and leave only the code inside the if.
    // Remove the whole if if you don't want this done for webforms either. And of course you
    // can add as many additional checks to the if as you like
    if (substr($details['%form_id'], 0, 7) == 'webform') { // If this is a webform
      $formName  = l($formName, substr($pageSubUrl, 1));
    }
  
  } else { // If no form was specified then use the location (without the main url)
    $formName  = l(t($pageSubUrl), substr($pageSubUrl, 1));
  }

  // Set up a display date and use it to control the appearance of the report by date.
  $shortDate  = format_date($captchaBlock['timestamp'], 'custom', "m/d/Y");

  // Processing by date.
  if ($shortDate == $currentDate) { // If we're processing an existing date
    $dateTotals[$shortDate]['count']++;
    if (isset($dateTotals[$shortDate]['formList'][$formName])) {
      $dateTotals[$shortDate]['formList'][$formName]++;
    } else {
      $dateTotals[$shortDate]['formList'][$formName]  = 1;
    }
  
    //$dailyOffenders[$captchaBlock['hostname']]++;
  } else { // Otherwise, if this is a new date
    $currentDate             = $shortDate; // Track the date
    $dateTotals[$shortDate]  = array('count' => 1, 'formList' => array($formName => 1));
    //$dailyOffenders = array();
  }
  
  // Track overall totals for a summary of what the attackers are hitting most
  $overallTotals[$formName]++;

/**
* ------------------------------------------------------------------------------
* Build report of counters by hostname.
* ------------------------------------------------------------------------------
*/
  // Set up the conter index depending on the type of attack.
  if (strpos($details['%form_id'], 'comment_form') !== false) { // If this is a comment
    $addToCounter  = 'comment';
  } elseif (strpos($details['%form_id'], 'user_register') !== false) { // If this is a webform
    $addToCounter  = 'reg';
  } elseif (substr($details['%form_id'], 0, 7) == 'webform') { // If this is a webform
    $addToCounter  = 'webform';
  } else {
    $addToCounter  = 'other';
  }

  // If this is the first time processing an IP address then set up the array of counters.
  if (! isset($hostTotals[$captchaBlock['hostname']])) {
    $hostTotals[$captchaBlock['hostname']]  = array('count' => 0, 'reg' => 0, 'comment' => 0, 'webform' => 0, 'other' => 0);
  }

  // Add to relevant counters for attacks from this IP address.
  $hostTotals[$captchaBlock['hostname']]['count']++; // Totals for this host
  $hostTotals[$captchaBlock['hostname']][$addToCounter]++; // Totals by attack
}

/**
* ------------------------------------------------------------------------------
* Go through the accumulated totals and build the primary report's detail.
* ------------------------------------------------------------------------------
*/
$captchaRows  = '';
foreach ($dateTotals as $reportDate => $totals) {
  $captchaRows  .= "<tbody>"; // Wrap each date cluster in its own table body

  // Set overall totals for the 1st 2 columns.
  $firstTwoColumns  = "
        <td>{$reportDate}</td>
        <td>" . number_format($totals['count'], 0, '', ',') . "</td>";

  // Produce a report line for each date+form combination, with
  // only the 1st line having date details. First sort the array
  // into alphabetical order by form, for consistency.
  ksort($totals['formList']);
  foreach ($totals['formList'] as $form => $formCount) {
    $captchaRows  .= "
      <tr>
        {$firstTwoColumns}
        <td>{$form}</td>
        <td>{$formCount}</td>
      </tr>";

    // First 2 columns are blank for all but the first row.
    $firstTwoColumns  = '
      <td>&nbsp;</td>
      <td>&nbsp;</td>';
  }
  $captchaRows  .= '</tbody>'; // Close the date cluster's table body
}

/**
* ------------------------------------------------------------------------------
* Build some additional useful information to show the user.
* ------------------------------------------------------------------------------
*/
// Build a summary of where the greatest attck volume is.
$summaryHeader  =
    t("Summary of %overall Recent Access Attempts Blocked by CAPTCHA in the last %start",
      array(
        '%overall' => number_format($overallCounter, 0, '', ','),
        '%start'   => $startingFrom
      )
    );
$summaryReport  = "";
arsort($overallTotals); // Sort by the counter so that the worst offenders appear 1st
foreach ($overallTotals as $attackedForm => $numberOfAttacks) {
  // Calculate a percentage.
  $rawPercentage   = round ( $numberOfAttacks / ( $overallCounter / 100 ), 2 );

  $nicePercentage  = number_format($rawPercentage, 0);

  $summaryReport  .= "
    <tr>
      <td>{$attackedForm}:</td><td>" . number_format($numberOfAttacks, 0, '', ',') . '</td><td>' . number_format($rawPercentage, 0) . '%</td>
    </tr>';
}

// Wrap the summary
$summaryReport  = "
    <table id='captcha_stats_summary' rules='groups' frame='box'>
      <thead>
        <tr>
            <th>" . t('Form/Page')   . "</th>
            <th>" . t('Attempts')    . "</th>
            <th>" . t('Approximate %')  . "</th>
        </tr>
      </thead>
      <tbody>
          {$summaryReport}
      </tbody>
    </table>";

// Collect some additional info on Captcha blocking, which is found in the Drupal
// variables table, and can be accessed with a standard Drupal function.
$captchaEngine    = variable_get('captcha_default_challenge', '**Unknown**');
$engineNameParts  = explode('/', $captchaEngine);

$totalSpamAttempts  = number_format(variable_get('captcha_wrong_response_counter', 0), 0, '', ',');
$sinceInception     =
  t("Since being activated, CAPTCHA has stopped %attempts. The default captcha engine is currently %engine.",
    array(
      '%attempts' => format_plural($totalSpamAttempts, '1 probable spam attempt', '@count probable spam attempts'),
      '%engine'   => $engineNameParts['1']
    )
  );

// Set a link to help users tweak logging settings.
$changeLoggingSettings  =
  t('If the report below shows you too much information or too little, then you might consider <a href="@settings-page">changing your logging settings</a>.',
    array('@settings-page' => url('admin/settings/logging/dblog'))
  );

// Set a link to an alternate way to view the log.
$viewLog  =  
  t('You can also <a href="@view-log-page">view recent log entries in a different way</a>.',
    array('@view-log-page' => url('admin/reports/dblog'))
  );

// Calculate the elapsed time covered and use it to calculate the average number
// of CAPTCHA blocks per day. In this case that is defined by
$timeDiffSeconds  = $lastTimestamp - $firstTimestamp;
$timeDiffDays     = (($timeDiffSeconds / 60) / 60 ) / 24;
$averagePerDay    = number_format($overallCounter / $timeDiffDays, 0, '', ',');
$displayAverage   =
  t('During this period there were an average of %average-attempts by CAPTCHA every day.',
    array('%average-attempts' => format_plural($averagePerDay, '1 block', '@count blocks')));

/**
* ------------------------------------------------------------------------------
* Complete building the primary report.
* ------------------------------------------------------------------------------
*/
if ($overallCounter) { // If we found anything to report on
  $mainReport  = "
    <p>{$displayAverage}</p>
    <table id='captcha_stats_report' rules='groups' frame='box'>
      <thead>
        <tr>
            <th>" . t('Date')       . "</th>
            <th>" . t('Attempts')   . "</th>
            <th>" . t('Form/Page')  . "</th>
            <th>" . t('Attempts')   . "</th>
        </tr>
      </thead>
      <tbody>
          {$captchaRows}
      </tbody>
    </table>";

  $mainReportHeader  = t("Summary Broken Down By Date");

/**
* ------------------------------------------------------------------------------
* Build a secondary report on hostnames (IP addresses). Many rows are hidden by
* default based thresholds of failure and lines. Change the following two
* variables to see a or less rows.
* ------------------------------------------------------------------------------
*/
  $blockThreshold  = 6;  // A threshold for which to hide rows with fewer CAPTCHA fails
  $lineThreshold   = 10; // A threshold for which to hide less pesky spammers
  $hostCnt         = 0;
  $odd             = true;
  $worstOffenders  = $otherOffenders  = '';

  arsort($hostTotals); // Sort by the counter so that the worst offenders appear 1st
  foreach ($hostTotals as $ipAddress => $ipTotals) {
    $hostCnt++;

    // Check to see if this IP address is already blocked from Drupal.
    $ipBlocked  = db_fetch_array(db_query("
      SELECT aid
        FROM {access}
       WHERE `type`='host'
         AND `status`=0
         AND `mask`='{$ipAddress}'
       LIMIT 1"));
  
    $blocked  = '';
    if ($ipBlocked['aid']) { // If we have already blocked this ip from drupal
      $blocked  = '(Blocked)';
    }
  
    // If we've finished building the report of the worst offenders then close it out.
    if (   ! $worstOffenders // If we have not already built the worst offender report
        && ($hostCnt >= $lineThreshold || $ipTotals['count'] < $blockThreshold)) { // and we're now processing less important hosts
      $worstOffenders  = wrapHostReportTable($hostRows); // Build the table.
      $hostRows        = ''; // Start from scratch for the 2nd report
    }

    // Do that thing Drupal does with odd and even rows, to aid CSS styling.
    if($odd) {
      $lineClass  = 'odd';
      $even       = false;
    } else {
      $lineClass  = 'even';
      $even       = true;
    }
  
    // We wrap the IP address in a link to an external service that will show you
    // details about the IP address. If you don't like this then remove the next
    // line, or feel free to make it link to a different service.
    $ipAddressLink  = l($ipAddress, "http://whatismyipaddress.com/ip/{$ipAddress}", array('attributes' => array('target' => '_blank', 'class' => 'ExternalLink')));

    $hostRows  .= "
      <tr {$lineStyles} class='{$lineClass}{$lineHideClass}'>
        <td class='captcha_col_1'>{$ipAddressLink}{$blocked}</td>
        <td class='captcha_col_2'>" . number_format($ipTotals['count'], 0, '', ',') . "</td>
        <td class='captcha_col_3'>" . number_format($ipTotals['reg'], 0, '', ',') . "</td>
        <td class='captcha_col_4'>" . number_format($ipTotals['comment'], 0, '', ',') . "</td>
        <td class='captcha_col_5'>" . number_format($ipTotals['webform'], 0, '', ',') . "</td>
        <td class='captcha_col_6'>" . number_format($ipTotals['other'], 0, '', ',') . "</td>
      </tr>";
  }

  // If we had enough spammers for 2 reports, then set up the second. The rest
  // are in a hidden DIV that can be revealed or re-hidden by clicking on a simple
  // jQuery toggle link. We do this to shorten what can be a very long list.
  if ($worstOffenders) { // If we have set up the 1st report
    if ($hostRows) { // If we have more rows to show
      $otherOffenders  = wrapHostReportTable($hostRows);
      $showMoreLink    = l(t('Show or Hide More Hosts'), $_REQUEST['q'], array('fragment' => 'hostRept', 'attributes' => array('name' => 'hostRept', 'onclick' => "$('#host_report_other').toggle('slow');")));
    }
  } else { // If we have not closed out the 1st report yet
    if ($hostRows) { // If we have rows to show
      $worstOffenders  = wrapHostReportTable($hostRows);
      $showMoreLink    = '';
    }
  }

  $hostReport  = "
    <div id='host_report_container'>
      <div id='host_report_worst'>
          {$worstOffenders}
      </div>
      <br />
      {$showMoreLink}
      <div id='host_report_other' style='display: none;'>
          {$otherOffenders}
      </div>
    </div>";

  $hostReportHeader  = t('Spammer Hosts, Starting With The Worst Offenders.');
  $hostReportText    = t('The following list shows up to @line-threshold potential spammers with at least @block-threshold form submissions blocked by CAPTCHA. ' .
                         'Click on the Show/Hide link to see all the IP Addresses. Click on any IP address to find out more about them. Those marked as blocked have ' .
                         'been <a href="@access-page">blocked from Drupal</a> (it\'s better to get your sysadmin to block them, but it gets the job done).',
                         array('@line-threshold' => $lineThreshold, '@block-threshold' => $blockThreshold, '@access-page' => url('admin/user/rules/list')));

} else { // If we found nothing to report on
  $mainReportHeader  = t('Unable to find any CAPTCHA or Mollom entries in your watchdog tables.');
  $mainReport        = $hostReport  = $hostReportHeader = '';
}

$mainHeader  =  t('Overview of CAPTCHA Spam Blocking');

/**
* ------------------------------------------------------------------------------
* Output the final reports. In a perfect world this would be in the theme layer.
* ------------------------------------------------------------------------------
*/
echo "
  <div id='captcha_stats_container'>
    <h3>
      {$mainHeader}
    </h3>
    <p>
      {$sinceInception} {$changeLoggingSettings} {$viewLog}
    </p>
    <br />
    <h3>
      {$summaryHeader}
    </h3>
    {$summaryReport}
    <br />
    <h3>
      {$mainReportHeader}
    </h3>
    {$mainReport}
    <br />
    <h3>
      {$hostReportHeader}
    </h3>
    <p>
      {$hostReportText}
    </p>
    {$hostReport}
  </div>
  <br />";


/**
* ------------------------------------------------------------------------------
* ============================================ Functions used by the above code.
* ------------------------------------------------------------------------------
*/

/**
* ------------------------------------------------------------------------------
* Function to wrap some table lines in a header.
* ------------------------------------------------------------------------------
* @param string $pLines - The lines to be wrapped in a table.
* @return string with HTML table
*/
function wrapHostReportTable($pLines) {
  return "
    <table class='host_stats_report' rules='groups' frame='box'>
      <thead>
        <tr>
            <th colspan='2'>" . t('Main')       . "</th>
            <th colspan='4'>" . t('Breakdown')  . "</th>
        </tr>
        <tr>
            <th>" . t('IP Address')   . "</th>
            <th>" . t('Attempts')     . "</th>
            <th>" . t('Registration') . "</th>
            <th>" . t('Comments')     . "</th>
            <th>" . t('Webforms')     . "</th>
            <th>" . t('Other')        . "</th>
        </tr>
      </thead>
      <tbody>
          {$pLines}
      </tbody>
    </table>";
}

The code I use right now inconjunction with the Honeypot module to autoban IP's.. If this could be turned into a function and inserted into the code above so you could just browse the report and click on them and their gone that would be cool.. Or if someone could dechiper the code above and put allow a variable of X attempts to be performed and the the the code below is executed that would be even better

$filename = '.htaccess'; // CHANGE IF NECESSARY
if ($fp = fopen($filename, 'a')) {
      if (flock($fp, LOCK_EX)) {
            $remote_addr = $_SERVER['REMOTE_ADDR'];
            fwrite($fp, "deny from $remote_addr\n");

      }

       flock($fp, LOCK_UN);
      fclose($fp);
}

header('HTTP/1.1 403 Forbidden');
echo "Forbidden!"; // Add any other HTML here
exit();

// EOF
geerlingguy’s picture

Thanks! For the future, though, try to stick bits of code like that in a file, and attach the file to your post—makes scrolling through these issues much easier :)

geerlingguy’s picture

Title: Automatically Ban Spammers and Bad Bots through redirect or in conjunction with Troll module. » Ban persistent spammers, possibly through integrating another module
Version: 6.x-1.13 » 7.x-1.x-dev
Priority: Major » Normal

Updating title to reflect what I'm thinking here; I could either write a little submodule that does an automated ban in certain situations, and has some configuration options there, or I could make sure there are easy ways for Honeypot to work with Troll, Ban User, or some other banning module.

I'm inclined to go down the route of a submodule, maybe honeypot_ban, which would integrate with Honeypot and allow configuration of how many times a certain IP would be logged as spam before being either permanently or temporarily banned.

zeroyon’s picture

I would say if you are going to go the route of a sub-module ie..honey-pot ban.. then perhaps you may as well make it universally popular and just call it bad-bot or ban bot.. and have it check the access logs for failed attempt, (honeypot, captcha etc) and ban based on the total number of failed attempts.. Seems simple enough.. Also if you going to go that route, integrate the report code I supplied earlier into the module so it shows honeypot failures as well, and you can one click write their IP to the .httaccess from there.. Sorry not trying to sound like I am ordering at a restaurant as I know you time I am sure is valuable, just tossing out ideas..

My cheap hack seemed to have an effect on the "honeypot related failures.. I think it's been five days and my .httaccess has 150+ entries.. see attachment and add to your own bad bot list if you choose..

Considering each one of those 150 or so IP's would have made 100's if not thousands of requests to my server, or jammed up the site battling captcha, and trying to register.. I would call it a success..

zeroyon’s picture

FileSize
3.47 KB

Would have helped if I would have actually attached the IP's in the previous post... list of bad spam bots I have caught trying to fill in honeypot over the last 5 days..

zeroyon’s picture

No word of any progress yet?

geerlingguy’s picture

@zeroyon - I simply haven't had the time for it lately, but I'd still like to do it someday. If anyone else would like to work on a separate module in the meantime, be my guest :)

dshields’s picture

not to be a pest, but this WOULD be a great submodule or independent module

geerlingguy’s picture

@dshields - you're not a pest at all! I like when people offer their input, since it shows me more people are interested in a particular feature. I'm leaning towards creating a new 'Honeypot Ban' module (separate from this module, so it's easier to maintain), and then adding this functionality there.

dshields’s picture

Awesome!
Your approach of a second module sounds like a good one.
It is certainly additional functionality that isn't required by many Honeypot users, so it's probably best to keep it as a separate project.
If there is anything I can do to help, please don't hesitate to ask.

a_lawry’s picture

Has any work started on this? If not I'd be happy to start development of a 'Honeypot Ban' module. Don't want to double up in case someone is already working on it.

geerlingguy’s picture

Not yet; be my guest!

dshields’s picture

a_lawry, that would be fantastic if you could get something going for 'Honeypot Ban' module!!

a_lawry’s picture

I've started looking at implementing the honepot ban module.

I have an admin form that sets a limit on form submissions and the period over which those form submission attempts occurred before blocking the IP address.

Given that you are already registering a flood event when honeypot blocks a form submission this seems like a good way to track which IPs have had form submissions blocked by honeypot and when.

flood_is_allowed() provides the test for how many times the flood event occured during a period of time.

So what is the best way to hook into honeypot to call the flood_is_allowed() when a form submission fails? It looks like hook_honeypot_add_form_protection($options, $form) is close but not quite what I would need. I could add my own validation function that calls flood_is_allowed() but it seems inefficient to do the check even when the form has passed the honeypot validation test.

a_lawry’s picture

Actually I think what we need is to make a hook_honeypot_failure($form_id, $type) that is called within _honeypot_log()

That way we can provide integration to other modules: eg.

honeypot_ban: Uses core IP banning system to block the IP address
honeypot_rules: Implements a rules event and triggers it using this hook

I'm happy to write the patch just wanted to get some feedback on the idea before going ahead further with it.

geerlingguy’s picture

I've opened #1969212: Add hook_honeypot_reject so other modules can react to rejected form submissions, so you'll have your hook in just a little bit.

geerlingguy’s picture

@a_lawry: I just committed #1969212-2: Add hook_honeypot_reject so other modules can react to rejected form submissions to Honeypot 7.x-dev. Please check it out and see if that unblocks your work on the Honeypot Ban module. Also, when you get an MVP of the module working, please create a Sandbox for it here on Drupal.org; I'll help review the project, and hopefully we'll be able to get it added as a full project (and it would be the first module you maintain on drupal.org, it seems!).

a_lawry’s picture

Thanks geerlingguy. I'm hoping to get a sandbox module up next weekend. I'll let you know when it is uploaded.

mgifford’s picture

What I like about Mollom is that they have a central database of bad users that have been collected & verified by humans. This is really quite powerful. I'm assuming that they also check other sources for blacklisted IPs.

Which got me looking around for Drupal projects that might address blacklisted IP's, and got me to http:BL, which uses the http://www.projecthoneypot.org as a central organizing database.

So how does the Drupal Honeypot module & the Project Honeypot relate to each other, other than that the like bees?

dshields’s picture

Any progress on Honeypot Ban?

zeroyon’s picture

?

geerlingguy’s picture

@zeroyon - could you put that list into a .txt file, delete the list from your last comment, and post the .txt file as an attachment to a new comment—it takes a while to scroll through this issue with the list posted inline.

zeroyon’s picture

FileSize
180.88 KB

No Problem was working on that, I realized what I did right after I put it up..

zeroyon’s picture

Issue summary: View changes

format

_paul’s picture

I made a really simple module for Drupal 7.x using the hook_honeypot_reject() to fire the drupal core function system_block_ip_action().

So if you are using honeypot and the drupal backoffice ip blocking functionnalities (So not throught the .htaccess), you can install this module http://www.paulplaye.fr/blog/sites/default/files/uploads/hive.tar.gz and find it's source code on the following drupal sandbox : https://drupal.org/sandbox/pwetosaurus/2148397.

I haven't yet read all of this post answers, and I doubt some better solutions weren't already posted here, but as this sandbox and custom module exist, I share it here.

Dave Reid’s picture

I've filed #2218537: Add Trigger and Rules event support as a related issue so that it would be super easy to set up the block IP action with honeypot rejection just with Drupal core's Trigger module or Rules.

geerlingguy’s picture

@_paul / #29 - the module looks fine, but may be a little heavy handed... if someone submits a form quickly and triggers the timestamp protection, they'd be immediately banned from the site. I think adding a little logic, like what I've outlined below, would be helpful to avoid false positives/potentially angry users:

1. If the user entered something in the honeypot field, go ahead and ban the IP. This should never happen unless it's a spammer.
2. If the user is caught by the honeypot timestamp protection, and the time is, say, 4x or 8x the limit (the honeypot_time_limit), go ahead and ban the IP. Otherwise, let this submission slide.

Users who fail the time limit test once should be allowed to keep trying. Users who fail 2, 3, or 4 times should be banned—and this should probably be configurable. Remember that the time limit increases exponentially every time a user tries submitting again!

@Dave Reid / #30 - I like your approach, which will allow those who need the ability to simply do things when a form is rejected to set everything up via Rules'/Trigger's UI. For many of the people in this thread, your patch will make things work well out of the box (no additional modules necessary!).

_paul’s picture

thanks,

I will make some improvements in order to by-pass time-limit honeypot rejection and use only the hidden field protection. Or use another time limit, like an arbitrary two seconds value.

Anyway, thanks for your review.

_paul’s picture

geerlingguy, following your thoughts about banning IP upon honeypot field submission (and no more on the time limit form protection), I made some changes in my sandbox.

Thanks for your comments and your work.

candelas’s picture

@geerlingguy what about checking about the ip from different attempts to register with different users. I have this problem and I go baning two a week at least. A user that makes a mistake registering is not going to change 3 times the nick. If that was configurable on Honeypot it would be great.

Thanks a lot for this module.

candelas’s picture

Other thing that I see that I will apply as a rule to block the ip is a user that tries
wordpress/wp-login.php
site/wp-login.php

or, since I have installed https://www.drupal.org/project/rename_admin_paths, any user trying to access user/login

candelas’s picture

Other thing would to ban any user that tries node/add and has not a role.

dshields’s picture

Re: #36
What if a client bookmarks the node/add page while familiarizing him or herself with drupal, then clicks that bookmark, assuming he or she is already logged in?
Your suggestion is to ban that ip?

candelas’s picture

Re: #37 If you have configured everything on the site before to go online, you will not have that problem. Yes I would like to be able to ban ips for that.

candelas’s picture

Re: #37 If the user tries to node/add and has not a role, then I would love to ban that ip :)

nithinkolekar’s picture

+1 for #34

along with rename_admin_paths another module internal nodes exist to prevent direct node/* accessing and by enabling this any user trying to view node/* will also be banned..

geerlingguy’s picture

Status: Active » Closed (outdated)

Closing this issue as it's been a long time with not much activity, plus there are a lot of other ways to implement the functionality as described in some of the issue comments.

denix’s picture

Hi @geerlingguy, as there is no solution yet to this, I think we should keep it open.
Also because other issues have been closed as duplicated of this one.