Repository navigation
For‐Developers
This technical reference is designed for developers extending or integrating with the CF7 Spam Checker plugin.
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
const LANGUAGE_PATTERNS = [
'russian' => [
'name' => 'Russian',
'regex' => '/^[А-Яа-яЁё\s\-]+$/u'
],
'english' => [
'name' => 'English',
'regex' => '/^[A-Za-z\s\-]+$/'
],
// ... additional languages
];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;const MAX_REQUESTS_PER_MINUTE = 20;
const BAN_THRESHOLD = 50; // Attacks before permanent ban
const BAN_DURATION = 3600; // Seconds (1 hour)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
add_action('plugins_loaded', [$this, 'initialize']);Early initialization ensures CF7 compatibility check and file system setup before form processing.
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.
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']);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)
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 brevityDevelopers must implement complete validation logic based on:
-
$this->settingsconfiguration - Field type detection via
$tag->name - Pattern matching against threats
- Rate limiting checks
const EVENT_TYPES = [
'SQL_INJECTION',
'XSS_ATTACK',
'CSRF_ATTEMPT',
'RATE_LIMIT_EXCEEDED',
'BOT_DETECTED',
'FILE_UPLOAD_VIOLATION',
'LANGUAGE_VALIDATION_FAILURE',
'MANUAL_BAN',
'IP_WHITELISTED'
];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": "192.168.1.100",
"reason": "Repeated SQL injection attempts",
"banned_at": 1737619200,
"expires_at": 1737622800,
"is_permanent": false
}
]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);
}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;
}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
}- Define constant in class
- Update dashboard UI to display new event type
- Call
logSecurityEvent()when event occurs
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;
}CF7_Advanced_Security::getInstance(): CF7_Advanced_SecuritySingleton accessor.
isContactForm7Active(): boolChecks if CF7 plugin is active.
loadSettings(): array
saveSettings(array $settings): bool
getBanList(): array
saveBanList(array $banList): bool
getWhiteList(): array
logSecurityEvent(string $type, array $data): void- 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)
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;
}// 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);- Submit CF7 form with malicious payloads
- Verify
security_incidents.jsoncontains event - Check IP appears in
ban_list.jsonafter threshold
Back to Home