Anti spam honey pot - Martin7483 - 09-30-2015
Hi,
I was searching on Google and in the CI forum but I could not find any recent topics on the use of a honey pot.
So I added it. If anyone is interested here are the steps and code required.
Any feedback is welcome
- Martin
Step 1: Edit config.php
Edit your application config.php file, located at ./application/config.php. Add the following lines to it
PHP Code: /* |-------------------------------------------------------------------------- | ANTI SPAM HONEY POT |-------------------------------------------------------------------------- | Enables the use of a invisible honey pot field. | | 'spam_protection' = TRUE => Use honey pot, FALSE => don't use honey pot | 'spam_protection_name' = The name of the honey pot cookie | 'honey_pot_expire' = The maximum time the token is valid for */ $config['spam_protection'] = TRUE; $config['spam_protection_name'] = 'ci_honey_pot'; $config['honey_pot_expire'] = 3600;
Step 2: Create MY_Input.php
Create MY_Input.php in your ./application/core directory and place the following code in it:
PHP Code: <?php if (! defined('BASEPATH')) exit('No direct script access allowed');
/** * My Input * * Extends CodeIgniters core Input with a honey pot anti-spam protection * Added config items for use. * * @author Martin Langenberg */
class MY_Input extends CI_Input { /** * Enable Honey pot flag * * Enables a honey pot token name to be set. * Set automatically based on config setting. * * @var bool */ protected $_enable_honey_pot = FALSE; public function __construct() { log_message('debug', "Class ".get_class($this)." Initialized."); parent::__construct(); $this->_enable_honey_pot = (config_item('spam_protection') === TRUE); $this->_resanitize_globals(); } // -------------------------------------------------------------------- /** * Resanitize Globals * * Internal method serving for the following purposes: * * - Sets the honey pot and validates on POST request * * @return void */ protected function _resanitize_globals() { // Honey pot protection check if ($this->_enable_honey_pot === TRUE && ! is_cli()) { $this->security->honey_pot_verify(); } log_message('debug', 'Honey pot set and verified'); } // -------------------------------------------------------------------- }
Step 3: Create MY_Security.php
Create MY_Security.php in your ./application/core directory and place the following code in it:
PHP Code: <?php if (! defined('BASEPATH')) exit('No direct script access allowed');
/** * My Security * * Extends CodeIgniters core Security with a honey pot anti-spam protection * * @author Martin Langenberg */
class MY_Security extends CI_Security { protected $_honey_pot_name; // The honey pot cookie name protected $_honey_pot_token_name; // The hashed honey pot field name protected $_honey_pot_expire; // The time the token remains valid for. Default is 1 hour public function __construct() { log_message('debug', "Class ".get_class($this)." Initialized."); parent::__construct(); $this->_honey_pot_name = config_item('spam_protection_name'); $this->_honey_pot_expire = config_item('honey_pot_expire'); } // -------------------------------------------------------------------- /** * Honey pot Verify * * @return MY_Security */ public function honey_pot_verify() { // If it's not a POST request, set the honey pot and return if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { return $this->_spam_protection_set_honey_pot(); } // It's a post, get the stored token name to use with the available $_POST data $this->_honey_pot_token_name = $_COOKIE[$this->_honey_pot_name]; // Do the tokens exist in the _POST if ( ! isset( $_POST[$this->_honey_pot_token_name] ) OR strlen($_POST[$this->_honey_pot_token_name]) > 0 ) // Is the honey pot empty? { // Log a clear error, but don't print clear honey pot errors to screen log_message('error', 'The honey pot was invalid OR not empty!'); $this->csrf_show_error(); } // We kill this since we're done and we don't want to polute the _POST array unset($_POST[$this->_honey_pot_token_name]); // Nothing should last forever unset($_COOKIE[$this->_honey_pot_name]); $this->_honey_pot_token_name = NULL; $this->_spam_protection_set_honey_pot(); log_message('info', 'Honey pot verified'); return $this; } // -------------------------------------------------------------------- /** * Set Honey pot name and value * * @return string */ protected function _spam_protection_set_honey_pot() { if ($this->_honey_pot_token_name === NULL) { // If the cookie exists we will use its value. // We don't necessarily want to regenerate it with // each page load since a page could contain embedded // sub-pages causing this feature to fail if (isset($_COOKIE[$this->_honey_pot_name]) && is_string($_COOKIE[$this->_honey_pot_name]) && preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_honey_pot_name]) === 1) { return $this->_honey_pot_token_name = $_COOKIE[$this->_honey_pot_name]; } $rand = $this->get_random_bytes(16); $this->_honey_pot_token_name = ($rand === FALSE) ? md5(uniqid(mt_rand(), TRUE)) : bin2hex($rand); } $this->honey_pot_set_cookie(); return $this->_honey_pot_token_name; } // -------------------------------------------------------------------- /** * Honey pot Set Cookie * * @return CI_Security */ public function honey_pot_set_cookie() { $expire = time() + $this->_honey_pot_expire; $secure_cookie = (bool) config_item('cookie_secure'); setcookie( $this->_honey_pot_name, $this->_honey_pot_token_name, $expire, config_item('cookie_path'), config_item('cookie_domain'), $secure_cookie, config_item('cookie_httponly') ); log_message('info', 'Honey Pot cookie sent'); return $this; } // -------------------------------------------------------------------- /** * return the set honey pot token name * @return string */ public function get_honey_pot_token_name() { return $this->_honey_pot_token_name; } // -------------------------------------------------------------------- }
Step 4: Create MY_form_helper.php
Create MY_form_helper.php in your ./application/helpers directory and place the following code in it:
PHP Code: <?php /** * Crossfire CMS * * @package Crossfire CMS * @author Martin Langenberg * @copyright Copyright (c) 2011 - 2015, Martin Langenberg * @since Version 2.0 * @filesource */
/** * * This class replaces the CodeIgniter form_helper method form_open(). * It adds the use of a invisible honey pot field if required by the config. * The hidden and invisible fields are wrapped in a invisible container. */
defined('BASEPATH') OR exit('No direct script access allowed');
/** * CodeIgniter Form Helpers * * @package CodeIgniter * @subpackage Helpers * @category Helpers * @author EllisLab Dev Team * @link http://codeigniter.com/user_guide/helpers/form_helper.html */
// ------------------------------------------------------------------------
if ( ! function_exists('form_open')) { /** * Form Declaration * * Creates the opening portion of the form. * * @param string the URI segments of the form destination * @param array a key/value pair of attributes * @param array a key/value pair hidden data * @return string */ function form_open($action = '', $attributes = array(), $hidden = array()) { $CI =& get_instance();
// If no action is provided then set to the current url if ( ! $action) { $action = $CI->config->site_url($CI->uri->uri_string()); } // If an action is not a full URL then turn it into one elseif (strpos($action, '://') === FALSE) { $action = $CI->config->site_url($action); }
$attributes = _attributes_to_string($attributes);
if (stripos($attributes, 'method=') === FALSE) { $attributes .= ' method="post"'; }
if (stripos($attributes, 'accept-charset=') === FALSE) { $attributes .= ' accept-charset="'.strtolower(config_item('charset')).'"'; }
$form = '<form action="'.$action.'"'.$attributes.">\n";
// Add CSRF field if enabled, but leave it out for GET requests and requests to external websites if ($CI->config->item('csrf_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method="get"')) { $hidden[$CI->security->get_csrf_token_name()] = $CI->security->get_csrf_hash(); } // Add honey pot field if enabled, but leave it out for GET requests and requests to external websites if ($CI->config->item('spam_protection') === TRUE && strpos($action, $CI->config->base_url()) !== FALSE && ! stripos($form, 'method="get"')) { $hidden[$CI->security->get_honey_pot_token_name()] = NULL; }
// Make all hidden fields invisible $form .= '<div class="hidden" style="display: none;">'; if (is_array($hidden)) { foreach ($hidden as $name => $value) { $form .= '<input type="hidden" name="'.$name.'" value="'.html_escape($value).'" style="display:none;" />'."\n"; } } $form .= '</div>';
return $form; } }
And that is that. You can now have a honey pot field automatically added to a form
RE: Anti spam honey pot - ignitedcms - 10-01-2015
Sorry what exactly is a honey pot?
RE: Anti spam honey pot - Martin7483 - 10-08-2015
(10-01-2015, 10:52 AM)iamthwee Wrote: Sorry what exactly is a honey pot?
A honey pot is an invisible form field that is required to be empty when the form is posted.
As bots fill in all available fields the form will be invalid and thus the posting of the form is made harder for bots.
RE: Anti spam honey pot - ardavan - 12-19-2015
Hey Martin,
thanks for sharing the codes.
I'm wonder you never mentioned about calling the helper anywhere. i didn't find any line of the code to call the helper as well !
Also im eager to know how am i able to test this functionality?
Please advice me.
Thanks
RE: Anti spam honey pot - skunkbad - 12-19-2015
This solution seems bloated. If you really want to have a so called "honeypot field" in your form, all you have to do is add it, then in your form validation add a callback that checks it for any value. If the value is there then die(), redirect(), log_something(), whatever();
RE: Anti spam honey pot - Martin7483 - 12-19-2015
(12-19-2015, 10:06 PM)skunkbad Wrote: This solution seems bloated. If you really want to have a so called "honeypot field" in your form, all you have to do is add it, then in your form validation add a callback that checks it for any value. If the value is there then die(), redirect(), log_something(), whatever();
Then you would always need to add it manually and remember too use a callback or custom rule. My approach enables this with a simple true / false setting just like csrf protection. Field is auto added and validated so no chance you would forget a step. If you think this is bloated you probebly think the csrf approach is bloated?
RE: Anti spam honey pot - Diederik - 12-20-2015
I like your approach, thanks for sharing.
@ardavan there is no need to make an extra call to the helper. It override the default form helper found in CodeIgniter. Normaly when you use the helper function form_open() CodeIgniter generates an opening html form tag and add some hidden fields to it containing the CSRF values. This addon also adds a honeypot field to it.
RE: Anti spam honey pot - includebeer - 12-20-2015
Interesting stuff. But why does it need a unique token? Am I wrong or is the same token used for all visitors?
RE: Anti spam honey pot - Martin7483 - 12-22-2015
(12-20-2015, 04:25 PM)includebeer Wrote: Interesting stuff. But why does it need a unique token? Am I wrong or is the same token used for all visitors?
First, you don't need a unique token. It is just an extra level of protection. If the form is recorded and a bot figures out that it should remain empty, it will fail because the recorded field is then invalid
Second, I missed that this way of generating and storing the token wouldn't work. So I have changed it so it now also uses a cookie just like CSRF uses a cookie. I have updaqted the original post with the new code.
RE: Anti spam honey pot - ardavan - 02-29-2016
Hey @Martin7483,
i copy and paste your honey pot code to my project.
Now How can i test whether its working on my project or not?
Thanks in advance
|