Anti-Spam Forms Revisited

Page 1 - Integrating with Akismet

Skip to navigation

Nov
11

Over a year ago I first spoke about methods to avoid spam submissions from forms. Now it's time to take another look at this with moderated comments, Project HoneyPot integration, and other methods.

If you're not using Wordpress or similar CMS then it's a little trickier to use Akismet on your site; but after a close look at their API it shouldn't be too bad. The first thing it requires is for you to register for a free Wordpress.com account which will give you an API key which will work with Akismet. I do find it kind of ironic that non-Wordpress users have to register with Wordpress in order to use Akismet... since they can have a free Wordpress blog in doing so.

Once you've done that then you'll need to modify your comment submission code - the bit where it gets stored in your database as you're either going to want to block it or flag it as spam. On my site I try not to block spam just in case it's falsely identified as spam, instead I mark it as spam and then every now and then check through the spam just to make sure. Normally I use curl to access remote sources, but in this case I'll go with the example that Akismet provide and shall utilise sockets. The code I use is as follows:

function _sendRequest ($path, $request)
{
  $request = 'blog=' . urlencode('some website URL') . '&' . $request;
  $http_request  = 'POST /1.1/' . $path . " HTTP/1.0\r\n";
  $http_request .= 'Host: ' . ($path!='verify-key'?$this->api_key.'.':'') .
                    AKISMET_API . "\r\n";
  $http_request .= "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
  $http_request .= 'Content-Length: ' . strlen($request) . "\r\n";
  $http_request .= 'User-Agent: ' . $this->user_agent . "\r\n";
  $http_request .= "\r\n";
  $http_request .= $request;
  $response = '';
  if (false !== ($fs = @fsockopen(AKISMET_API, 80, $errno, $errstr, 3))) {
    fwrite($fs, $http_request);
    while (!feof($fs)) {
      $response .= fgets($fs, 1160);
    }
    fclose($fs);
    $response = explode("\r\n\r\n", $response, 2);
  }
  return $response;
}

This is part of a class which also contains other functions for interacting with Akismet where AKISMET_API is a constant defined as rest.akismet.com - this is a constant as it's easier to change if they ever move the location of their API. You should also notice that there are two properties of the class being used here - a User-Agent string and a property containing the API key to use.

As the blog URL is required on all requests the function appends the blog before sending it. The HTTP request is then built up by specifying the script URL to POST to, the hostname to send the request to (including API key for most requests), the length of the request, the content type, a user-agent and the request itself. The user-agent should be sent as something which identifies your CMS and plugin version in the following format:

CMS_NAME/CMS_VERSION | PLUGIN_NAME/PLUGIN_VERSION

As an example, I send the following User-Agent from entity CMS 2.0:

$akismet->user_agent = 'entity CMS/' . BUILD_VERSION . ' | Akismet/1.0'; 

One you've understood how these requests are made it is now a good point to build the class to put the function inside of. This needs to consist of some properties, a constructor, and eventually a few more member functions. So for the class we might have something like:

/**
 * Class used for handling Akismet integration
 * @package ext
 */
class Akismet {
  /**
   * The API key to use for requests
   * @var string
   */
  var $api_key;
  /**
   * The user agent to send as the source of the API request
   * @var string
   */
  var $user_agent;
  /**
   * Any optional parameters to send with the request to better identify the comment
   * @var string[]
   */
  var $optional_params;
  /**
   * Default constructor function for the Akismet class
   * @param string $api_key The API key to authenticate with
   */
  function Akismet ($api_key, $user_agent = '')
  {
    $this->api_key = $api_key;
    $this->user_agent = $user_agent;
    $this->optional_params = array(
        'comment_type' => '',
        'comment_author' => '',
        'comment_email' => '',
        'comment_url' => '',
        'referrer' => '',
        'permalink' => '');
  }
}

It's a very basic class and even once the previous _sendRequest function is added to it there is still nothing that complicated. So let's move on to adding some more of the functions that will actually interact with Akismet.