How to protect your WordPress forms against spam

The reason I started writing this post is because I see a lot of people searching the internet for solutions to protect their WordPress forms from spam. If you are using a plugin like Contact form 7 or Gravity forms you can use for example the Akismet plugin. But if you build your own custom made HTML forms I will give you some other solutions you could try.

Possible solutions:

  1. Blacklist the spam words
  2. Check the IP address against a DNS Blacklist

Solution 1: Blacklist the spam words

This solution is an effective one if you know what kind of spam you already have received in the past and when your form contains a message field.

The idea is that you create a list of words that possible be spam words (like casino, investing, viagra etc.). Then you compare those words to the message input of the submitted form. When the input contains one of the words, you do not store the submission in your database.

Create settings field to store blacklist words

So first let’s create a field in our WordPress admin to be able to add the spam blacklist words. Add the following code to your functions.php:

/**
 * Adds theme settings page under general settings tab
 */
function th_admin_menu() {
   add_options_page( __( 'Theme' ), __( 'Theme' ), 'manage_options', 'theme-settings', 'th_theme_settings_page' );
}
add_action( 'admin_menu', 'th_admin_menu' );

/**
 * Adds and registers blacklist words field
 */
function th_admin_init() {

   // Spam protection section
   $section = 'spam_protection_settings_section';
   add_settings_section( $section, __( 'Spam protection' ), 'th_spam_protection_settings_section_callback', 'theme-settings' );

   // Spam blacklist words field
   $id = 'spam_words';
   add_settings_field( $id, __( 'Spam words' ), 'th_textarea_settings_field', 'theme-settings', $section, array(
      'id' => $id,
      'description' => __( 'A comma seperated list of possible spam words' )
   ));
   register_setting( 'theme-settings', $id );

}

/**
 * Spam protection settings section callback
 */
function th_spam_protection_settings_section_callback() {
   echo '<p>' . __( 'Spam protection settings.' ) . '</p>';
}

/**
 * Settings textarea field callback
 */
function th_textarea_settings_field( $args ) {

   $value = get_option( $args['id'] );

   $html = '<textarea type="text" class="regular-text" name="' . $args['id'] . '">' . $value . '</textarea>';

   if ( isset( $args['description'] ) ) {
      $html .= '<p class="description">' . $args['description'] . '</p>';
   }
   echo $html;

}

Now check for spam words

Let’s say you have a contact form with 3 fields like this:

<form method="post">

   <div class="form-row">
      <label><?php _e( 'Name' ); ?></label>
      <input type="text" name="input[name]">
   </div>

   <div class="form-row">
      <label><?php _e( 'Email' ); ?></label>
      <input type="text" name="input[email]">
   </div>

   <div class="form-row">
      <label><?php _e( 'Message' ); ?></label>
      <input type="text" name="input[message]">
   </div>

   <div class="form-row">
      <input type="submit" name="submit" value="<?php _e( 'Submit' ); ?>">
   </div>

</form>

Now let’s handle the form on the init hook and check for any blacklisted spam words.

/**
 * Handles forms submission
 */
function th_handle_form() {

   // Form submitted
   if ( ! isset( $_POST['submit'] ) )
      return;

   // Look for blacklist spam words
   if ( get_option( 'spam_words' ) ) {

      // Convert comma seperated string to array of spam words
      $spam_words = explode( ',', str_replace( ', ', ',', get_option( 'spam_words' ) ) );

      if ( ! empty( $spam_words ) ) {

         // Check whether any spam word exists in the message input field
         if ( preg_match('/' . implode( '|', $spam_words ) . '/i', $_POST['input']['message'] ) ) {
            die( __( 'We suspect that you are a robot...' ) );
         }

         // No spam, handle rest of your form...
      }
   }
   
}
add_action( 'init', 'th_handle_form' );

Each time a spam bot submit one of the blacklisted words in the message field of form now, the script dies and no spam is stored in your database.

Solution 2: Check the IP address against a DNS Blacklist

Another effective way to protect your web forms against spammers is by checking the visitor’s IP address against a DNS Blacklist. Below I will explain how to do this.

Obtain the IP address

First you have to obtain the IP address of the visitor that is submitting the form. You can simply do this by adding a hidden field to your HTML form which holds the IP address as value.

<form method="post">

   <!-- ...Other form fields -->
   
   <div class="form-row">
      <input type="submit" name="submit" value="<?php _e( 'Submit' ); ?>">
   </div>

   <!-- Holds the visitor's IP address -->
   <input type="hidden" name="input[ip]" value="<?php echo get_client_ip_env(); ?>">

</form>

As you can see I used a function instead of $_SERVER['REMOTE_ADDR'] to print the IP address. This will ensure you to have the biggest change to obtain a correct IP address. The get_client_ip_env() function I used looks as follows:

/**
 * Function to get the client ip address
 */
function get_client_ip_env() {

    $ipaddress = '';
    if ( getenv('HTTP_CLIENT_IP') )
        $ipaddress = getenv('HTTP_CLIENT_IP');
    else if ( getenv('HTTP_X_FORWARDED_FOR') )
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
    else if ( getenv('HTTP_X_FORWARDED') )
        $ipaddress = getenv('HTTP_X_FORWARDED');
    else if ( getenv('HTTP_FORWARDED_FOR') )
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
    else if ( getenv('HTTP_FORWARDED') )
        $ipaddress = getenv('HTTP_FORWARDED');
    else if ( getenv('REMOTE_ADDR') )
        $ipaddress = getenv('REMOTE_ADDR');
    else
        $ipaddress = 'UNKNOWN';

    return $ipaddress;
}

DNSBL check

When the IP address is known you can check it against a DNS Blacklist. You have many blacklist lookups which you can find and test here. Thanks to xnite’s DNS Blacklist lookups PHP Tutorial I have got a clear solution on how to work with these DNS lookups in PHP.

/**
 * Checks whether IP is DNS blacklisted
 *
 * @param string $ip_address the ip address to check
 * @param string $dnsbl the host of the dns blacklist
 */
function dnsbl_check( $ip_address = NULL, $dnsbl = 'dnsbl-1.uceprotect.net' ) {

	if ( $ip_address == NULL ) {
		/* No IP address given */
		return false;
	}

	if ( filter_var($ip_address, FILTER_VALIDATE_IP ) === false ) {
		/* IP address is invalid */
		return false;
	}

	if ( $dnsbl == NULL ) {
		/* No DNSBL to check against */
		return false;
	}

	/* Need to reverse the IP address */
	$array = explode( ".", $ip_address );
	$array = array_reverse( $array );
	$reverse_ip = implode( ".", $array );

	/* Perform the check */
	$res = gethostbyname( $reverse_ip.".".$dnsbl );

	if ( $res == $reverse_ip . "." . $dnsbl ) {
		/* IP is not in given DNSBL */
		return false;
	}

	/* No checks failed, hostname does not match request, IP is in DNSBL */
   return true;
}

The only thing you have left to do now is implementing the dnsbl_check() function in your form handling script.

/**
 * Handles forms submission
 */
function th_handle_form() {

   // Form submitted
   if ( ! isset( $_POST['submit'] ) )
      return;

   // Check ip address is blacklisted
   if ( ! empty( $_POST['input']['ip'] ) ) {

      $lists = ['all.s5h.net']; // Add all blacklists you want to check here 
      foreach ( $lists as $list ) {

         if ( dnsbl_check( $_POST['input']['ip'], $list ) == true ) {
            die( __( 'We suspect that you are a robot...' ) );
         }
      }
   }

   // No spam, handle rest of your form...
      
}
add_action( 'init', 'th_handle_form' );

If the dnsbl_check() returns true it means the IP address is possibly spam in this DNS blacklist lookup.

I will keep you updated

I will update this post each time I find new smart solutions to protect your WordPress website from spammers. If you have better solutions share them in the comments below.

I'm Robbert Vermeulen. I document everything I learn and help thousands of people with coding WordPress. My site has no ads or sponsors. If you enjoy my content, please consider supporting what I do.

Comments

    No comments yet..

Leave a reply

Feel free to ask questions or make comments.