Break a failed-token spike down by source
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
);
Hooks Used
Need this built rather than just documented? See our services →