Brute force attack prevention#26026
Conversation
- exponential delay for repeated failures per hostname and user, max delay 1 minute - limit the number of concurrent requests that delay to 3 per the same hostname and user - if requests are coming from localhost (Admin Console), take the hostname from a special header - ignore requests with empty password - a lot of tools (e.g. IDEs) issue regular pings with an empty password
|
0b2a8a3 to
cd56c9c
Compare
8529bfb to
cc74b67
Compare
cc74b67 to
98db0ce
Compare
avpinchuk
left a comment
There was a problem hiding this comment.
How much memory will be consumed to store login attempts when there are a large number of attempts?
The memory will keep teh records only for 1 minute, so it will hold as many records as can be generated in 1 minute, a record per each different username. it could be improved to either keep a record only per remote IP address but I wanted to avoid that to avoid complete lock out in case an attacker and and regular users would come from the same host (e.g. if proxy is used without the Another option to improve this is to somehow find out whether the username is valid and store attempts for invalid usernames under the same key. I'll think about this and see if there's a simple way to find out whether the username exists, not not just whether the username/password combination is valid. |
| data.lastFailureTime.set(System.currentTimeMillis()); | ||
|
|
||
| // Calculate exponential delay: 2^(failures-1) seconds, capped at MAX_DELAY_SECONDS | ||
| int delaySeconds = (int) Math.min(Math.pow(2, failureCount - 1), MAX_DELAY_SECONDS); |
There was a problem hiding this comment.
Once the delay reaches MAX_DELAY_SECONDS after 6 failures, aren’t we repeatedly doing redundant Math.pow calculations and incrementing failureCount without any practical effect?
It might be better to cap the failure count at a realistic number (e.g. 10–20 attempts), and after that simply keep using a fixed delay of MAX_DELAY_SECONDS.
There was a problem hiding this comment.
Thank you. I adjusted the algorithm to skip computing Math.pow if failureCount is greater than square root of the wait limit.
|
@avpinchuk , I adjusted the tracking algorithm to track unknown usernames under the same key to prevent the hashmap from unlimited growth. |
d85d266 to
ffdc3c6
Compare
| int delaySeconds = (int) Math.min(Math.pow(2, failureCount - 1), MAX_DELAY_SECONDS); | ||
| // Calculate exponential delay | ||
| int delaySeconds = failureCount - 1 < FAILURE_COUNT_REACHING_MAX_DELAY | ||
| ? (int) Math.pow(2, failureCount - 1) |
There was a problem hiding this comment.
When failureCount=7, delaySeconds becomes 64 seconds, which exceeds MAX_DELAY_SECONDS. It's safer to keep Math.min().
- ? (int) Math.pow(2, failureCount - 1)
+ ? (int) Math.min(Math.pow(2, failureCount - 1), MAX_DELAY_SECONDS)There was a problem hiding this comment.
Thank you, @hs536 . Done, I amended my last commit.
- avoid computing square root when max attempts reached - store non-existent usernames under the same key to prevent username enumeration attacks and unbounded tracker map growth
ffdc3c6 to
c016af8
Compare
|
@OndroMih, let me confirm a few points. Is my understanding of the user impact from introducing this security feature correct as follows?
|
|
Hi, @hs536 , yes, your understanding is correct, plus:
This feature is enabled by default because regulations require that GlassFish is secure in the default configuration. Idesigned it carefully so that an attacker cannot lock out valid users:
If this is not enough, I can also add an option to disable this feature with an additional toggle on the HTTP config, which would be enabled by default. |
If needed, let's do that in a followup PR. Additionally we could factory out some of the locking code to the authentication lib. |
protection against brute-force authentication attacks on the administration interface (both the Administration Console and REST API). This protection works by:
This mechanism makes it impractical for attackers to try many passwords in rapid succession while allowing legitimate users to retry after a short wait.
This mechanism applies only for remote connections. Local connections are never delayed.
When the server is deployed behind a reverse proxy, the configuration option
behind-proxyon the admin listener enables getting remote client's address from proxy headers.