Skip to content

For‐Developers

Mikhail Deynekin edited this page Jan 23, 2026 · 1 revision

Developer Documentation

This technical reference is designed for developers extending or integrating with the CF7 Spam Checker plugin.

Architecture Overview

Core Class: CF7_Advanced_Security

The plugin is implemented as a single-class architecture with modular validation methods.

File: cf7_check.php

Key Components:

  • Singleton pattern for global instance management
  • Hook-based integration with WordPress and Contact Form 7
  • JSON-based state persistence (no database tables)
  • Event-driven logging system

Constants and Configuration

Language Pattern Definitions

const LANGUAGE_PATTERNS = [
    'russian' => [
        'name' => 'Russian',
        'regex' => '/^[А-Яа-яЁё\s\-]+$/u'
    ],
    'english' => [
        'name' => 'English',
        'regex' => '/^[A-Za-z\s\-]+$/' 
    ],
    // ... additional languages
];

Field Validation Limits

const MIN_NAME_LENGTH = 2;
const MAX_NAME_LENGTH = 50;
const MAX_EMAIL_LENGTH = 100;
const MAX_TEXT_LENGTH = 5000;
const MAX_FILE_SIZE_MB = 10;

Rate Limiting Parameters

const MAX_REQUESTS_PER_MINUTE = 20;
const BAN_THRESHOLD = 50;  // Attacks before permanent ban
const BAN_DURATION = 3600;  // Seconds (1 hour)

File System Structure

Log Directory: CF7FC_LOG_DIR (typically /wp-content/uploads/cf7-security/)

State Files:

  • settings.json — Plugin configuration
  • ban_list.json — Blacklisted IPs with metadata
  • white_list.json — Whitelisted IPs
  • security_incidents.json — Event log entries

WordPress Hooks

Initialization Hook

add_action('plugins_loaded', [$this, 'initialize']);

Early initialization ensures CF7 compatibility check and file system setup before form processing.

Admin Hooks

add_action('admin_menu', [$this, 'addAdminMenu']);
add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets']);

Registers admin pages under CF7 Security menu and loads Tailwind CSS + custom assets.

AJAX Endpoints

All AJAX handlers require manage_options capability and nonce verification.

add_action('wp_ajax_cf7fc_save_settings', [$this, 'ajaxSaveSettings']);
add_action('wp_ajax_cf7fc_unban_ip', [$this, 'ajaxUnbanIp']);
add_action('wp_ajax_cf7fc_make_permanent', [$this, 'ajaxMakePermanent']);
add_action('wp_ajax_cf7fc_clear_expired_bans', [$this, 'ajaxClearExpiredBans']);
add_action('wp_ajax_cf7fc_manual_ban', [$this, 'ajaxManualBan']);
add_action('wp_ajax_cf7fc_add_whitelist', [$this, 'ajaxAddWhitelist']);
add_action('wp_ajax_cf7fc_remove_whitelist', [$this, 'ajaxRemoveWhitelist']);

Contact Form 7 Integration

Validation Hook Registration

add_filter('wpcf7_validate_text', [$this, 'validateField'], 20, 2);
add_filter('wpcf7_validate_text*', [$this, 'validateField'], 20, 2);
add_filter('wpcf7_validate_email', [$this, 'validateField'], 20, 2);
add_filter('wpcf7_validate_email*', [$this, 'validateField'], 20, 2);
add_filter('wpcf7_validate_textarea', [$this, 'validateField'], 20, 2);
add_filter('wpcf7_validate_textarea*', [$this, 'validateField'], 20, 2);

Priority: 20 (executes after CF7's built-in validators at priority 10)

Validation Method Signature

public function validateField($result, $tag): WPCF7_Validation
{
    // $result: WPCF7_Validation object (mutable)
    // $tag: WPCF7_FormTag object (field metadata)
    
    // Implementation logic here
    
    return $result;
}

Important: The current implementation in cf7_check.php contains a placeholder comment:

// Validation logic truncated for brevity

Developers must implement complete validation logic based on:

  • $this->settings configuration
  • Field type detection via $tag->name
  • Pattern matching against threats
  • Rate limiting checks

Security Event Logging

Event Types

const EVENT_TYPES = [
    'SQL_INJECTION',
    'XSS_ATTACK',
    'CSRF_ATTEMPT',
    'RATE_LIMIT_EXCEEDED',
    'BOT_DETECTED',
    'FILE_UPLOAD_VIOLATION',
    'LANGUAGE_VALIDATION_FAILURE',
    'MANUAL_BAN',
    'IP_WHITELISTED'
];

Logging Function

private function logSecurityEvent(string $eventType, array $data): void
{
    $event = [
        'timestamp' => time(),
        'event_type' => $eventType,
        'ip' => $this->clientIp,
        'user_agent' => $this->userAgent,
        'data' => $data
    ];
    
    // Append to security_incidents.json
    // Apply log retention policy
}

Usage Example:

$this->logSecurityEvent('SQL_INJECTION', [
    'field_name' => 'message',
    'pattern_matched' => 'SELECT',
    'form_id' => $form->id()
]);

IP Access Control

Ban List Structure

[
  {
    "ip": "192.168.1.100",
    "reason": "Repeated SQL injection attempts",
    "banned_at": 1737619200,
    "expires_at": 1737622800,
    "is_permanent": false
  }
]

Programmatic IP Ban

private function banIp(string $ip, string $reason, int $duration): bool
{
    $banList = $this->getBanList();
    
    $banList[] = [
        'ip' => $ip,
        'reason' => $reason,
        'banned_at' => time(),
        'expires_at' => time() + $duration,
        'is_permanent' => ($duration === 0)
    ];
    
    return $this->saveBanList($banList);
}

IP Check Before Validation

private function isIpBanned(string $ip): bool
{
    $banList = $this->getBanList();  // Auto-purges expired
    
    foreach ($banList as $ban) {
        if ($ban['ip'] === $ip) {
            if ($ban['is_permanent']) return true;
            if ($ban['expires_at'] > time()) return true;
        }
    }
    
    return false;
}

Extending the Plugin

Custom Validation Rules

To add custom validation logic without modifying core:

Step 1: Hook into validation filters at lower priority

add_filter('wpcf7_validate_text', 'my_custom_validator', 10, 2);

function my_custom_validator($result, $tag) {
    $value = isset($_POST[$tag->name]) ? trim($_POST[$tag->name]) : '';
    
    if (str_contains($value, 'forbidden_word')) {
        $result->invalidate($tag, 'This content is not allowed.');
    }
    
    return $result;
}

Step 2: Integrate with CF7 Spam Checker logging

if (class_exists('CF7_Advanced_Security')) {
    $security = CF7_Advanced_Security::getInstance();
    // Access public methods if available
}

Adding New Event Types

  1. Define constant in class
  2. Update dashboard UI to display new event type
  3. Call logSecurityEvent() when event occurs

Custom Rate Limiting

Implement session-based or cookie-based tracking:

function check_custom_rate_limit($ip) {
    $transient_key = 'cf7_rate_' . md5($ip);
    $count = get_transient($transient_key);
    
    if ($count === false) {
        set_transient($transient_key, 1, 60);  // 1 minute
        return true;
    }
    
    if ($count >= 5) {
        return false;  // Exceeded limit
    }
    
    set_transient($transient_key, $count + 1, 60);
    return true;
}

API Reference

Public Methods

CF7_Advanced_Security::getInstance(): CF7_Advanced_Security

Singleton accessor.

isContactForm7Active(): bool

Checks if CF7 plugin is active.

Protected/Private Methods (Internal Use)

loadSettings(): array
saveSettings(array $settings): bool
getBanList(): array
saveBanList(array $banList): bool
getWhiteList(): array
logSecurityEvent(string $type, array $data): void

Performance Considerations

JSON File I/O Optimization

  • Lazy Loading: Files read only when accessed
  • Write Buffering: Single write per request for settings/bans
  • Expiry Cleanup: Performed during read operations (no cron required)

Caching Strategy

Consider implementing object caching for high-traffic sites:

function get_cached_ban_list() {
    $cache_key = 'cf7fc_ban_list';
    $ban_list = wp_cache_get($cache_key);
    
    if ($ban_list === false) {
        $ban_list = $this->getBanList();
        wp_cache_set($cache_key, $ban_list, '', 300);  // 5 minutes
    }
    
    return $ban_list;
}

Testing

Unit Testing Fields

// Test language validation
$validator = new CF7_Advanced_Security();
$result = new WPCF7_Validation();
$tag = (object)['name' => 'your-name'];
$_POST['your-name'] = 'Тест123';  // Invalid (mixed)

$result = $validator->validateField($result, $tag);
assert($result->is_valid($tag) === false);

Integration Testing

  1. Submit CF7 form with malicious payloads
  2. Verify security_incidents.json contains event
  3. Check IP appears in ban_list.json after threshold

Back to Home