WordPress.org

Make WordPress Core

Opened 12 months ago

Last modified 3 weeks ago

#24193 reopened enhancement

Anti brute force protection

Reported by: MAzZY Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 3.5.1
Component: Users Keywords: has-patch
Focuses: Cc:

Description

To protect against hacking should be added to the login form protection from brute force. Now the password can enter an unlimited number of times. It is necessary to limit the number of login attempts.

For example, some variants:
1) Captcha after three unsuccessful attempts
2) Temporary inability to login after three unsuccessful attempts
3) something else

Attachments (5)

sleep.patch (601 bytes) - added by mpol 12 months ago.
add a sleep of 3 seconds if the login failed.
24193.diff (3.3 KB) - added by ericlewis 3 weeks ago.
24193.1.diff (6.2 KB) - added by ericlewis 3 weeks ago.
24193.2.diff (7.9 KB) - added by ericlewis 3 weeks ago.
security.php (3.3 KB) - added by Denis-de-Bernardy 3 weeks ago.

Download all attachments as: .zip

Change History (27)

comment:1 knutsp12 months ago

1) Captcha is not wanted. Really bad UX.
2) Inability for whom? If per IP it doesn't work for coordinated attacks. If per user anyone can cause the site owner to be blocked. At also generates huge login logs, especially when attacked (a possible DoS attack). Not good.
3) Use a strong password. Some plugins may also limit the login attempts, if that is what you want. A plugin may also hide the login form for robots (obscurity).

The best approach for core is to make it very difficult, or almost impossible, to set a weak password. Strong passwords are real security. There is a feature request for this in ticket #21737.

comment:2 johnbillion12 months ago

There are a lot of complexities involved in preventing brute force attacks, which is why I suggest that this remains in the realm of plugins.

Just one example: I work in an office and everyone working there shares the same IP address. This means that we cannot block an IP address if someone enters an incorrect password X times, because you're not going to be popular when you lock out the entire office.

mpol12 months ago

add a sleep of 3 seconds if the login failed.

comment:3 mpol12 months ago

I'm sure some ideas are possible, not all ideas are bad or unworkable. I like the Captcha idea after 3 failed login attempts.
Another idea is to wait 3 seconds if the password is wrong. Just like SSH does. I'll attach a patch for that (very simple).
I'm sure other ideas are welcome here. There's not a single solution, but some ideas will make it for the attackers less convenient.

comment:4 knutsp12 months ago

I'm afraid that a PHP sleep for some seconds is out of the question for core. It could very easy bring down servers due to overload. At least I fear that.

A captcha, while quite bad and hated, after n failed attempts on same user and IP, could be an acceptable solution.

We should then make a plugin as "proof of concept". It should have no options, but hooks so it's behaviour can be modified by (other) plugins.

A good, simple, clean, well-written plugin may have a chance of being accepted for core consideration.

  1. Must not consume a lot of resources, especially under an attack
  2. Must have neglectible impact when logging the failed login attempts
  3. Must never lock out legitimate users
  4. Must not let anyone making trouble for other users
  5. May be differentiate between users that have a strong password and those who have not
  6. Should provide fallbacks or options for other login plugins like "Sidebar Login"

We must not forget that WordPress has millions of installations, mostly on shared hosting, and we have big multisites. A core thing will be the default for all. In times of attacks it must not make things worse, and it can easily do so.

I'm willing to contribute to such a plugin if we reach consensus on how it should work. Comments from core developers would be very useful.

But in making a plugin, there is a chanche it will have to stay as "yet another login security plugin", and the whole thing regarded as "plugin territory".

comment:5 markoheijnen12 months ago

  • Keywords close added

I recommend for closing this ticket. There should be a better way of protecting but what this ticket is about isn't the way.
Having a strong password makes sense and should be encouraged.

comment:6 c3mdigital8 months ago

  • Keywords close removed
  • Resolution set to wontfix
  • Status changed from new to closed

Plugin realm. See comments

comment:7 SergeyBiryukov8 months ago

  • Milestone Awaiting Review deleted

comment:8 ericlewis3 weeks ago

  • Resolution wontfix deleted
  • Status changed from closed to reopened

Drupal has had something like this in place since 2009. Maybe we can learn something from their experience.

It doesn't seem too technically challenging to me, and could be a valuable shield against (at least amateur) brute force attacks that users won't even notice.

ericlewis3 weeks ago

comment:9 ericlewis3 weeks ago

attachment:24193.diff is a proof of concept.

It uses an options hash to track number of failed login attempts per IP. If more than 10 failed login attempts happen within a minute, the user is throttled for the rest of that minute.

comment:10 ericlewis3 weeks ago

  • Keywords has-patch added

comment:11 nacin3 weeks ago

Two main problems with this:

  • This would quickly balloon the options table and probably crash it. In fact it'd be a fairly effective attack in its own right.
  • It would do nothing to prevent distributed brute-force attempts. One person with huge botnet could trivially do some serious damage with this. You'd need to do per-user stuff, rather than per-IP.
  • OK, three problems. Per-user is tough because then it'd be easy to block a user from logging in legitimately. In fact, it'd be a fairly effective attack in its own right.

It's really, really hard to get this right. That's why every plugin I've seen offers a serious amount of configuration, as if a user is going to know how to best balance legitimate attempts versus dealing with a distributed botnet. It's a terrible, horrible user experience.

comment:12 knutsp3 weeks ago

The protection must be both per IP and per username.

The default limit must be fairly high, like at least 10 attempts (filterable, of course)

A block on username should cause a email to be sent to the user, allowing to unblock and login once again (using a secret key in the url), or request changing the password.

It's important that a legitimate user may not be blocked by others attacking the account using his/her username. But we have the stored email address and the ability to send an email.

A "last successful login IP" could also be stored for every login, as a one-or-few-item whitelist for each user.

There is no need to save all the failed login attempts. What is needed is the number of failed attempts, for a while, maybe is an incremented number. Any successful login should delete the log for both IP and user.

I am quite sure we can work this out!

Last edited 3 weeks ago by knutsp (previous) (diff)

comment:13 nacin3 weeks ago

If we have the ability to send an email (which we may *not*, if the server is not configured right), then we really should be doing two-factor auth via IP whitelisting via email.

comment:14 follow-up: knutsp3 weeks ago

Not being able to send email is an edge condition, making trouble from the installation and up.

Some sort of whitelisting via email, yes. Some sort of a hard protecting using email as a way around for legitimate users being locked out by a special kind of attack on this protection system itself.

Now we are talking.

comment:15 in reply to: ↑ 14 ; follow-up: SergeyBiryukov3 weeks ago

Replying to knutsp:

Not being able to send email is an edge condition, making trouble from the installation and up.

FWIW, it's not uncommon on support forums.

comment:16 SergeyBiryukov3 weeks ago

  • Milestone set to Awaiting Review

comment:17 in reply to: ↑ 15 ; follow-up: knutsp3 weeks ago

Replying to SergeyBiryukov:

FWIW, it's not uncommon on support forums.

is it possible to detect that email are not sent?

comment:18 ericlewis3 weeks ago

How about a supplemental CAPTCHA method that is required after X # of failed login attempts within Y amount of time?

ericlewis3 weeks ago

comment:19 ericlewis3 weeks ago

attachment:24193.1.diff is a work in progress towards CAPTCHA-based throttling.

Currently implemented so that after a 10th failed attempt on a user account within 2 minutes, a captcha is displayed on the wp-login page.

There's a lot more to do here. The form submission doesn't even verify the captcha result at this point. However, I think it's a step in the right direction re: Nacin's comments.

  • We only need to update the DB a specific number of times (the number of login attempts allowed) to keep a tally of login attempts within the throttling period.
  • Throttling is user-based, not IP-based.
  • Users will never be locked out, they just need to fill out a CAPTCHA as well as their credentials.
Last edited 3 weeks ago by ericlewis (previous) (diff)

comment:20 Denis-de-Bernardy3 weeks ago

Fwiw, I've a quick hack running on my own sites using the following approach:

  1. On login attempt, get the latest_logins user_meta. It's an array of array(date, ip, success) that keeps track of up to three successful login attempts, and up to first three failed ones that occurred after the last successful successful login.
  1. If the last three login attempts are all failed, it locks the user by triggering the reset password procedure alongside setting a user_locked meta. The user_locked meta denies any login attempt, successful or not, until the password is indeed reset (at which point the user_locked meta is deleted).
  1. The user's profile page lists the lastest_logins in a table for information.

Most of the code is related to 3. Points 1 and 2 are maybe 10-20 lines of code, since they're using existing APIs.

---

Edit: bit more than 10-20 lines, actually… Attaching the relevant file for reference.

Last edited 3 weeks ago by Denis-de-Bernardy (previous) (diff)

ericlewis3 weeks ago

comment:21 ericlewis3 weeks ago

attachment:24193.2.diff is a working prototype.

Create and store a captcha image in wp-includes/images/, which gets refreshed every ten seconds. There's probably a better place for this, as I know some folks lock down permissions on core folders.

Store an md5 hash of the captcha phrase as a site option. When a user is throttled, show the captcha image, an input field, and a hidden input with the md5 captcha to compare their input against.

Last edited 3 weeks ago by ericlewis (previous) (diff)

Denis-de-Bernardy3 weeks ago

comment:22 in reply to: ↑ 17 SergeyBiryukov3 weeks ago

Replying to knutsp:

is it possible to detect that email are not sent?

I guess it is since #23642 is fixed. See also #23291.

Note: See TracTickets for help on using tickets.