Init Sentinel – Part 2: The Core Function for Logging Security Events in WordPress

After establishing the database foundation, the real value of Init Sentinel comes from its core logging function. This is where unauthorized access attempts are transformed into structured, controlled, and actionable security data.

Init Sentinel – Part 2: The Core Function for Logging Security Events in WordPress

This article breaks down the Init Sentinel core function and highlights the key engineering decisions behind it: when to log, when to stay silent, how to prevent log spam, and how to keep the system lightweight over time.

The Role of the Core Function in Init Sentinel

The core function is not just a database insert. It acts as a central control gate that determines which events are worthy of becoming security logs.

  • Provides a global on/off switch for logging via filters.
  • Excludes legitimate administrator and editor activity.
  • Normalizes and sanitizes input data before persistence.
  • Prevents duplicate logs from bloating the database.

Every monitoring, alerting, or response layer built on top of Init Sentinel depends on the discipline enforced at this level.

Definition of init_html_log_security_event

The Init Sentinel core function follows the init_html_* prefix convention, ensuring consistency across functions, filters, and actions.

/**
 * Core: Log a security event into the `init_sentinel_security_log` table
 * Filters:
 * - init_html_sentinel_enabled(bool $enabled, string $endpoint, string $action, int $status_code): global enable/disable
 * - init_html_sentinel_retention_days(int $days): log retention period in days (default 30)
 */
function init_html_log_security_event( $endpoint, $action, $status_code = 403 ) {
    // 0) Global enable/disable via filter
    $enabled = (bool) apply_filters('init_html_sentinel_enabled', true, (string) $endpoint, (string) $action, (int) $status_code);
    if ( ! $enabled ) {
        return;
    }

    // Skip logging for Editor+ (Editor / Admin / Super Admin)
    if ( current_user_can('edit_others_posts') ) {
        return;
    }

    global $wpdb;

    // 1) Collect user, IP and UA (no enrichment, no geo)
    $user_id    = get_current_user_id() ?: null;
    $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? substr((string) $_SERVER['HTTP_USER_AGENT'], 0, 500) : '';

    // Prefer real client IP when behind proxy/CDN
    $ip = 'unknown';
    if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $ip = trim($parts[0]);
    } elseif (!empty($_SERVER['REMOTE_ADDR'])) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }

    // 2) Prevent log spam (same IP + endpoint within 1 minute)
    $table  = $wpdb->prefix . 'init_sentinel_security_log';
    $exists = $wpdb->get_var( $wpdb->prepare(
        "SELECT EXISTS(
            SELECT 1 FROM {$table}
            WHERE ip_address = %s
              AND endpoint   = %s
              AND created_at > (UTC_TIMESTAMP() - INTERVAL 1 MINUTE)
        )",
        $ip, $endpoint
    ) );

    if ( $exists ) {
        return;
    }

    // 3) Insert log entry using UTC timestamps
    $wpdb->insert(
        $table,
        [
            'user_id'     => $user_id,
            'ip_address'  => $ip,
            'endpoint'    => substr((string) $endpoint, 0, 255),
            'action'      => substr((string) $action,   0, 100),
            'status_code' => (int) $status_code,
            'user_agent'  => $user_agent,
            'created_at'  => gmdate('Y-m-d H:i:s'),
        ],
        ['%d','%s','%s','%s','%d','%s','%s']
    );

    $log_id = (int) $wpdb->insert_id;

    // 4) Probabilistic log cleanup based on retention period (1% chance)
    if ( mt_rand(1,100) === 1 ) {
        $days = (int) apply_filters('init_html_sentinel_retention_days', 30);
        if ($days < 1) { $days = 1; } if ($days > 3650) { $days = 3650; }
        $wpdb->query( "DELETE FROM {$table} WHERE created_at < (UTC_TIMESTAMP() - INTERVAL {$days} DAY)" );
    }

    // 5) Extension hook
    do_action( 'init_html_security_event', $log_id, [
        'user_id'     => $user_id,
        'ip_address'  => $ip,
        'endpoint'    => (string) $endpoint,
        'action'      => (string) $action,
        'status_code' => (int) $status_code,
        'user_agent'  => $user_agent,
        'created_at'  => gmdate('Y-m-d H:i:s'),
    ] );
}

Global Filters and Behavioral Control

The init_html_sentinel_enabled filter allows logging to be conditionally disabled based on endpoint, action, or status code, making Init Sentinel adaptable to complex environments.

Log Spam Prevention and Database Protection

Using an EXISTS query within a short time window prevents redundant log entries during scans or brute-force attempts, keeping the database clean and performant.

UTC Storage and Cleanup Without Cron Jobs

All timestamps are stored in UTC to avoid timezone inconsistencies. Cleanup is triggered probabilistically, eliminating the need for scheduled cron tasks.

Extension Hooks and Security Reactions

The init_html_security_event action serves as an integration point for advanced responses such as alerts, dashboards, or automated IP blocking.

Conclusion

This core function is the heart of Init Sentinel. It prioritizes discipline, performance, and extensibility, ensuring the security system remains effective without becoming a liability.

The next article will cover where and how to hook Init Sentinel into WordPress to log 403 errors in the correct execution context.

Comments


  • No comments yet.

Init Toolbox

Press Ctrl + \ on desktop, or swipe left anywhere on mobile.

Login