1

I'd like to get your advice about following:

I have web-application on Tomcat using jsp, where users after entering their login and password (both are defined automatically and not changed after account creation) are able to enter their personal page.

I want to make some protection for user accounts using ArrayList on server with users' login ids, where amount of not successful logins for some login ids will be hold (there will be thread making amount value zero after some time period).

In case amount is bigger than some defined value - block login (until amount cleaned) and send to user email link, after clicking on which amount value will be set to 0 internally in server. I will work on that, but my question is about if this approach is correct one and such ArrayList will satisfy needs:

List<User> users = Collections.synchronizedList(userList);

and access it using synchronized set and get methods.
The aim is to get protection against brute-force attacks (manual or maybe even server driven).

Is there a way to defend against access attacks (making many login attempts in short periods of time)?

Thanks in advance.

SoldierKB
  • 49
  • 1
  • 2
  • 11
  • 1
    [CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA)? – nfechner Dec 08 '15 at 09:17
  • @nfechner That's possible, thanks for idea. But I'm looking for other solutions as well. – SoldierKB Dec 08 '15 at 09:25
  • using `synchronized` wont get you any degree of protection against brute-force attacks. At all. Because that would'nt even make any sense. You need to limit HTTP requests / second / IP to a sensible amount - and stop returning data if the threshold is reached. This combined with re-captcha will protect your pages somewhat – specializt Dec 08 '15 at 09:32
  • @specializt `synchronized` is needed cuz there will be reads from `List users` and new values added there too. – SoldierKB Dec 08 '15 at 09:46

3 Answers3

1

and access it using synchronized set and get methods.

How exactly you suppose to get User from such list? Either during every login attempt you have to iterate over whole list to find that User or you always know exact position in the array or may be the list is sorted so you can use binary search but in this case insertion would be inefficient in terms of complexity. Also the User structure or wherever you want to store latest failed logins, must be synchronized.

Imho one of the simplest solutions in terms of realization could be the following. Note that no additional threads are needed to reset number of attempts after some time period, but ones in a while you have to clear pairs where the most recent failed login time is beyond observable time frame. Cause other way the structure could grow in size.

ConcurrentHashMap<String, List<Long>> loginFails = new ConcurrentHashMap<>();
int ATTEMPTS_TO_FREEZE = 5;
int TIME_FRAME_IN_MINUTES = 5;

with login as key, and list of couple last failed logins as value. Let the threshold be 5 in our case. Before login check permission. If 5 fails in last 5 minutes -> reject

List<Long> attempts = loginFails.get(login);
if (attempts != null) {
    synchronized(attempts) {
        if (attempts.size() == ATTEMPTS_TO_FREEZE 
            && attempts.peek() > System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(TIME_FRAME_IN_MINUTES)) {
        //return some warning to user, that he exceeded number of attempts
        }
    }
}

After failed login

Queue<Long> attempts = loginFails.get(login);
if (attempts == null) {
    attempts = loginFails.putIfAbsent(login, new LinkedList<Long>());
    if (attempts == null) {
        attempts = loginFails.get(login);
    }
}
synchronized (attempts) {
    attempts.add(System.currentTimeMillis());
    if (attempts.size() > ATTEMPTS_TO_FREEZE) {
        attempts.remove();
    }
}

On reset (after clicking on emailed link) you can simply remove entry with such login. Instead of String key you can also store id or whatever (but make sure the key class has equals hashcode contract and is immutable)

Also note that this scheme wouldn't be very efficient if someone tries to crack lots of logins. And there might be some mistakes in code, but I hope you took the point.

dezhik
  • 990
  • 10
  • 16
  • Looks great, thx. Every User has id, and it will be used in hashCode() and equals() methods, so looking for `user` object with id specified in array should be fast. – SoldierKB Dec 08 '15 at 11:39
  • hashCode is not used in ArrayList or LinkedList or any other List collections (where you can have duplicates), it is used in hash based collections such as HashSet, HashMap – dezhik Dec 08 '15 at 11:56
  • But I will use it to get `user` object from array, like `users.get(logUser)` - that way it'll take small time to find this object in array. Or am I wrong? – SoldierKB Dec 08 '15 at 12:06
  • `logUser` should be an int, cause in List get is defined this way `E get(int index);`. And how your `logUser` would be calculated? – dezhik Dec 08 '15 at 12:23
  • Yes, you are right. I'd better use `ConcurrentHashMap` with data concerning users' logins and monitoring their login attempts. – SoldierKB Dec 08 '15 at 13:11
1

I don't know java enough to propose you code.

However, if you could store requests with IP address, you could put special rules on it (like firewall).

Say, when the same IP sends 100 requests in a minute, you can ban it for accessing your web site.

A good advice already given is to add delay before answering. If millions of passwords must be tested to find a valid one, each second you add makes the brute force attack very time consuming.

Iggy
  • 53
  • 8
0

A simple thing that can easily protect against brute force attacks is to add a salt and pepper. These are essentially random characters attached to passwords and/or usernames which make the odds of brute force succeeding MUCH lower.

This and this talks more about salt and peppers in detail.

This wikipedia article discusses the general foundations of the methodology.

Community
  • 1
  • 1
Filipe Teixeira
  • 3,565
  • 1
  • 25
  • 45
  • Thx for suggestion, great info. I'll use it in another application, but here logins and passwords are predefined by server and pretty simple for users convenience. – SoldierKB Dec 08 '15 at 10:16
  • You can still use this approach and users will be able to type in normal clear text passwords and usernames. The **salt** and **pepper** are hidden from the user. It's attached to the username/password during the registration/login password. The idea is that adding random characters makes passwords far less likely to be brute forced. Most systems should be using **salt** (at least) regardless of the use case. – Filipe Teixeira Dec 08 '15 at 10:19
  • But how will it help from manual brute-force attack if password is pretty simple (lets say 5 digits)? I want to make it hard to crack user password that way. – SoldierKB Dec 08 '15 at 11:00
  • I recommend reading [this](https://crackstation.net/hashing-security.htm) it describes perfectly how salts add to security. The basic idea is: 1. User registers with password `password = bob`. 2. At the time of registration you generate a **salt** `salt = t372ri3fiuv` 3. You prepend the salt to the password and hash: `h = hash(salt + password)` 4. You then save the `salt` and the hashed `h` password to the database. 5. Next time the user logs in you prepend the saved salt, recompute the hash and check if there is a match. This is step 1 towards making a more secure login system. – Filipe Teixeira Dec 08 '15 at 11:58
  • In my case **salt** usage has no meaning because password is generated by server and contains digits only. I want to make some protection against manual brute-force elsewhere I'd simply change the way passwords are generated to get more complicated ones. – SoldierKB Dec 08 '15 at 13:17
  • From my understanding of your system a **salt** may still be useful as the password (wether user generated or not) is still public knowledge. Salt's are always secret (They are never exposed), hence the extra layer of protection. [This](https://www.addedbytes.com/blog/why-you-should-always-salt-your-hashes/) explains how salting is kind of becoming security 101. – Filipe Teixeira Dec 08 '15 at 13:27