Log Failed Logins
Keeps a log of the last 200 failed login attempts with username, IP address and time, viewable under Tools > Failed Logins.
Opens WPIDE on
Code
// How many entries to keep.
$wpide_failed_log_max = 200;
add_action( 'wp_login_failed', function ( $username ) use ( $wpide_failed_log_max ) {
$log = get_option( 'wpide_failed_logins', array() );
$log = is_array( $log ) ? $log : array();
$log[] = array(
'time' => time(),
'user' => substr( sanitize_user( (string) $username ), 0, 60 ),
'ip' => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '',
);
update_option( 'wpide_failed_logins', array_slice( $log, -$wpide_failed_log_max ), false );
} );
add_action( 'admin_menu', function () {
add_management_page( 'Failed Logins', 'Failed Logins', 'manage_options', 'wpide-failed-logins', function () {
if ( isset( $_POST['wpide_clear_failed'] ) && check_admin_referer( 'wpide_clear_failed' ) ) {
delete_option( 'wpide_failed_logins' );
}
$log = array_reverse( (array) get_option( 'wpide_failed_logins', array() ) );
echo '<div class="wrap"><h1>Failed Logins</h1>';
echo '<form method="post">';
wp_nonce_field( 'wpide_clear_failed' );
echo '<p><button class="button" name="wpide_clear_failed" value="1">Clear log</button></p></form>';
echo '<table class="widefat striped"><thead><tr><th>Time</th><th>Username tried</th><th>IP address</th></tr></thead><tbody>';
if ( ! $log ) {
echo '<tr><td colspan="3">No failed logins recorded.</td></tr>';
}
foreach ( $log as $row ) {
printf(
'<tr><td>%1$s</td><td>%2$s</td><td><code>%3$s</code></td></tr>',
esc_html( wp_date( 'Y-m-d H:i:s', (int) $row['time'] ) ),
esc_html( $row['user'] ),
esc_html( $row['ip'] )
);
}
echo '</tbody></table></div>';
} );
} );
Notes
The log is stored in the wpide_failed_logins option (not autoloaded).
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.