Skip to main content

Break a failed-token spike down by source

Free Intermediate

Core counts failed lookups per show only — deliberately, since a per-IP counter would let an attacker rotating IPs create unbounded storage. The IP hash travels in the hook’s $context so you can keep your own per-source tally where your storage can take it. Only tokens matching no subscriber row fire this; a churned subscriber’s app polling a revoked token never does.

ip_hash is salted with benecaster_ip_hash_salt — it identifies a repeat source but cannot be reversed to an address. Never log $_SERVER['REMOTE_ADDR'] alongside it; the plugin’s security checklist forbids storing raw IPs.

Code

<?php
add_action(
    'benecaster_invalid_token_recorded',
    function ( int $show_id, int $current_count, array $context ): void {
        $source = $context['ip_hash'] ?? 'unknown';
        $key    = 'my_bad_token_' . $show_id . '_' . substr( (string) $source, 0, 16 );

        // Your own short-lived per-source tally. One transient per
        // source: on a site without a persistent object cache these are
        // wp_options rows, so keep the TTL short.
        $hits = (int) get_transient( $key ) + 1;
        set_transient( $key, $hits, HOUR_IN_SECONDS );

        // One source responsible for most of the show's failures in the
        // window looks like a scraper or a guessing run, not a typo.
        if ( $hits >= 20 && $hits * 2 >= $current_count ) {
            my_ops_alert( sprintf(
                'Show #%d: %d failed token attempts in %d min, %d from one source (%s)',
                $show_id,
                $current_count,
                $context['window_minutes'],
                $hits,
                $context['country_code'] ?? '??'
            ) );
        }
    },
    10,
    3
);

View on GitHub →

Hooks Used

Need this built rather than just documented? See our services →