PHP Forms Anti-Spam Library - an Easy Anti Spam solution

A defensive security PHP library that provides multiple layers of protection against form spam without requiring annoying CAPTCHAs. The library uses a combination of honeypot fields, submission timing analysis, RBL (Real-time Blackhole List) checks, and optional Redis-based rate limiting.

Table of Contents

Features

Requirements

Installation

Include the library in your PHP project:

<?php
require_once("antispam/EasyAntiSpam.php");
use awehttam\AntiSpam\EasyAntiSpam;
?>

Quick Start

1. Initialize EasyAntiSpam

<?php
@session_start(); // Required for form timestamping

require_once("antispam/EasyAntiSpam.php");
use awehttam\AntiSpam\EasyAntiSpam;

$antiSpam = new EasyAntiSpam("contactForm");
?>

2. Add Timestamp When Rendering Form

<form method="post">
    <?php $antiSpam->timestampForm(); ?>

    <!-- Your form fields -->
    <input type="text" name="name">
    <input type="email" name="email">

    
    <input type="text" name="phone" style="display:none">

    <button type="submit">Submit</button>
</form>

3. Check Submission for Spam

if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $score = $antiSpam->scoreSubmission($_POST, "phone");

    if($score) {
        // Spam detected - show generic error
        echo "An error occurred processing your request";
    } else {
        // Process legitimate submission
        // Send email, save to database, etc.
    }
}

Using EasyAntiSpam

Basic Usage

<?php
@session_start();
require_once("antispam/EasyAntiSpam.php");
use awehttam\AntiSpam\EasyAntiSpam;

// Create instance with unique form identifier
$antiSpam = new EasyAntiSpam("myForm");

// When rendering the form
$antiSpam->timestampForm();

// When processing submission
if($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Check for spam (returns 0 if clean, positive number if spam detected)
    $score = $antiSpam->scoreSubmission($_POST, "honeypot_field");

    if($score) {
        $errors[] = "Sorry, we can't process your request";
    }
}
?>

Multiple Honeypot Fields

// Check multiple hidden fields at once
$score = $antiSpam->scoreSubmission($_POST, ["phone", "website", "company"]);

Configuration Options

// Enable debug logging
$antiSpam->setDebug(true);

// Use custom RBL servers
$antiSpam->setRBLServers([
    'zen.spamhaus.org',
    'bl.spamcop.net',
    'dnsbl.sorbs.net'
]);

// Use custom DNS server for RBL lookups
$antiSpam->setDNSServer("1.1.1.1:53");

Detailed Spam Scoring

// Get detailed score without fast-check (checks all conditions)
$score = $antiSpam->scoreSubmission($_POST, "phone", false);

if($score == 0) {
    echo "Clean submission";
} else if($score == 1) {
    echo "One spam indicator detected";
} else {
    echo "Multiple spam indicators: " . $score;
}
Tip: The third parameter $fastcheck defaults to true, which returns immediately on the first spam indicator. Set to false to accumulate all spam scores.

Using RedisRatelimiter

Basic Rate Limiting

<?php
require_once("antispam/RedisRatelimiter.php");
use awehttam\AntiSpam\RedisRatelimiter;

// Connect to Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// Check rate limit: max 5 submissions per 10 minutes (600 seconds)
$exceeded = RedisRatelimiter::ratelimit(
    $redis,
    $_SERVER['REMOTE_ADDR'],
    600,  // time window in seconds
    5     // max hits in that window
);

if($exceeded) {
    die("Too many requests. Please try again later.");
}
?>

Rate Limiting with Callback

// Custom callback when rate limit is exceeded
$callback = function($info) {
    error_log("Rate limit exceeded: " . $info['total_user_calls'] .
              " calls in " . $info['time_period'] . " seconds");

    // Could also:
    // - Send alert email
    // - Log to monitoring system
    // - Update abuse database
};

$exceeded = RedisRatelimiter::ratelimit(
    $redis,
    $_SERVER['REMOTE_ADDR'],
    600,
    5,
    $callback
);

if($exceeded) {
    header('HTTP/1.1 429 Too Many Requests');
    die("Rate limit exceeded");
}

Combining with EasyAntiSpam

<?php
session_start();
require_once("antispam/EasyAntiSpam.php");
require_once("antispam/RedisRatelimiter.php");
use awehttam\AntiSpam\EasyAntiSpam;
use awehttam\AntiSpam\RedisRatelimiter;

$antiSpam = new EasyAntiSpam("contactForm");

if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $errors = [];

    // First: Check rate limit
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    if(RedisRatelimiter::ratelimit($redis, $_SERVER['REMOTE_ADDR'], 300, 3)) {
        $errors[] = "Too many requests. Please wait before trying again.";
    }

    // Second: Check spam indicators
    $score = $antiSpam->scoreSubmission($_POST, "phone");
    if($score) {
        $errors[] = "Your submission could not be processed.";
    }

    if(empty($errors)) {
        // Process legitimate submission
        mail($to, $subject, $message);
        echo "Thank you! Your message has been sent.";
    } else {
        foreach($errors as $error) {
            echo $error . "<br>";
        }
    }
}
?>

Configuration Options

EasyAntiSpam Methods

Method Parameters Description
__construct() string $formName Creates instance with unique form identifier
timestampForm() None Records current time in session for this form
scoreSubmission() array $vars, string|array $hiddenFields, bool $fastcheck Checks submission for spam indicators. Returns spam score.
setDebug() bool $mode Enables/disables debug logging
setRBLServers() array $servers Sets custom list of RBL servers to query
setDNSServer() string $server Sets custom DNS server (format: "host:port")

RedisRatelimiter Methods

Method Parameters Description
ratelimit() Redis $redis, string $ip, int $expires, int $max_hits, callable $callback Checks if IP has exceeded rate limit. Returns true if exceeded.

RBLChecker Configuration

The RBLChecker is automatically used by EasyAntiSpam but can also be used independently:

<?php
require_once("antispam/RBLChecker.php");
use awehttam\AntiSpam\RBLChecker;

$checker = new RBLChecker();
$checker->dnsServer = "8.8.8.8:53";
$checker->overallTimeoutMS = 300;  // 300ms timeout
$checker->maxRandomServers = 10;   // Query up to 10 RBL servers
$checker->debug = true;

$hits = $checker->checkRBL($_SERVER['REMOTE_ADDR']);
if($hits) {
    echo "IP found on $hits blacklists";
}
?>

Complete Example

See example.php for a complete working contact form with EasyAntiSpam integration. The example demonstrates:

Key Points from example.php

<?php
session_start();
require_once("antispam/EasyAntiSpam.php");
use awehttam\AntiSpam\EasyAntiSpam;

$antiSpam = new EasyAntiSpam("exampleForm");

if($_SERVER['REQUEST_METHOD']=='POST'){
    $errors=[];

    // Validate form fields
    if(!$_POST['name']) {
        $errors[] = "Please include your name";
    }

    // Check for spam
    $score = $antiSpam->scoreSubmission($_POST, "phone");
    if($score){
        $errors[] = "An internal error occurred";  // Generic message
    }

    if(empty($errors)){
        // Process submission
        mail($to, $subject, $message);
        echo "Thank you! Your message has been sent.";
    }
}
?>

<form method="post">
    <?php $antiSpam->timestampForm(); ?>

    <input type="text" name="name" placeholder="Your Name">
    <input type="email" name="email" placeholder="Your Email">

    <!-- Hidden field (honeypot) - Bots will fill this field.  Note: You should not include this comment in your real form -->
    <input type="text" name="phone" style="display:none">

    <textarea name="message"></textarea>
    <button type="submit">Submit</button>
</form>
Security Best Practice: Always use generic error messages when spam is detected. Don't reveal the specific anti-spam technique that triggered, as this helps spammers circumvent your protections.

How It Works

1. Honeypot Detection

Hidden form fields that legitimate users won't see or fill, but bots will. If populated, the submission is flagged as spam.

2. Timing Analysis

Forms are timestamped when rendered. If submission happens too quickly (default: <2,000 milliseconds / 2 seconds), it's likely automated.

3. RBL Checking

The submitter's IP is checked against multiple Real-time Blackhole Lists using concurrent non-blocking UDP queries. This happens in ~250ms and queries up to 7 random RBL servers by default.

4. Rate Limiting (Optional)

Using Redis, track submission frequency per IP address. If an IP exceeds the threshold (e.g., 5 submissions in 10 minutes), block further attempts.

Debugging

Enable debug mode to see detailed logging in your PHP error log:

$antiSpam->setDebug(true);

// Now submissions will log:
// - ANTISPAM DEBUG: Post hidden field score: 0
// - ANTISPAM DEBUG: Post minimum typing time score: 0; typing time was 15 seconds
// - ANTISPAM DEBUG: Post RBL Check score 0
Performance Note: RBL checking uses fast concurrent queries with a 250ms timeout. Even checking multiple RBLs typically completes in under 300ms.

PHP Forms Anti Spam - Defensive security library for web forms