1

Currently, wordpress default is returning 200 on either failed or successfull login attempt. The modern standard, used by all web application framework is to return 401 (or 403) on failed login attempts.

This allows third party tools (think waf, fail2ban, etc) to detect brute forcing attempt and block it from outside of wordpress.

I can't find where I can make this change or is there a plugin providing such a functionality.

Yes, I'm well aware of plugins who attempt to provide "brute force blocking" from inside of Wordpress. But besides being a problem on their own, they are prone to being shut from inside the Wordpress installation. And the defence is placed in the wrong level. Instead of being a perimeter defense, all those requests hit the actual wordpress Installation. So no, this isn't a good option for me.

Thanks for the help!

2
+25

WordPress handles login failed in two ways:

  1. If it is a bad credential, and both username and password have a value, then this action can be captured by wp_login_failed

  2. If both, or one, of the options are empty, then WordPress generates the error object as the first parameter in the authenticate filter; it does not open and wp_login_failed action captures this cause/event For what we have done here,

see comments in code:

add_filter( 'authenticate', function( $user, $username, $password ) {
    // forcefully capture login failed to forcefully open wp_login_failed action, 
    // so that this event can be captured
    if ( empty( $username ) || empty( $password ) ) {
        do_action( 'wp_login_failed', $user );
    }
    return $user;
} );

// to handle even you can handle the error like
add_action( 'wp_login_failed', function( $username ) {
    if ( is_wp_error( $username ) ) {
        // you return 401 with wp function, this action takes place before header sent.
        $wp_query->set_401();
        status_header( 401 );
        nocache_headers();

    }
} );

my answer is a combination of : Redirect user using the 'wp_login_failed' action hook if the error is 'empty_username' or 'empty_password' and How to force a 404 on WordPress

update: I wrote super simple plugin to do this WP-401-On-Failed-Login. It uses some wp auth hooks, and set_header() before content being sent.

  • 1
    There doesn't appear to be a set_401() function in the WP_Query class, so this won't work, I'm afraid. – Pat J Nov 25 '16 at 4:38
  • @amit-rahav thanks for answer, I suspect there is a bug in the code: Look like the 401 would be executed and sent only if there is an error on $username. But what if there is another error (Notably wrong password). Would it run? Or perhaps the variable the callback gets isn't really a username? Also: why do we also need $wp_query --> set_401() and does it even exist? – alonisser Nov 25 '16 at 13:06
1

If you have access to the server your site is running on, you could use modsecurity to return 401 from the web server (instead of through PHP).

Here's a tutorial I wrote a while back about blocking IPs and returning 401 through modsecurity:

https://smyl.es/how-to-block-wp-login-php-brute-logins-with-cpanel-mod-security-and-configserver-firewall/

  • This is based on apache. I use nginx. Although the 200 for wp-login can work perhaps? But quite annoying I can't get the correct result – alonisser Nov 29 '16 at 18:16
  • Yes, a 200 response means failed login, 302 means valid as it's issuing a redirect after verifying the login and setting cookie – sMyles Nov 29 '16 at 18:19
  • The reason you wouldn't want a 401 is because WordPress outputs the error on the wp-login page, thus is why it's a 200 instead of 302, because it just reloads the page showing the error instead of doing a redirect. That's why the modsecurity tutorial only returns a 401 after like 10 failed logins – sMyles Nov 29 '16 at 18:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.