Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for anti-brute force #207

Open
jwag956 opened this issue Nov 8, 2019 · 3 comments
Open

Add support for anti-brute force #207

jwag956 opened this issue Nov 8, 2019 · 3 comments

Comments

@jwag956
Copy link
Collaborator

jwag956 commented Nov 8, 2019

OWASP https://github.com/OWASP/ASVS/blob/master/4.0/en/0x11-V2-Authentication.md#v21-password-security-requirements 2.2.1 talks about brute force mitigation:

Verify that no more than 100 failed attempts per hour is possible on a single account.

This can probably be implemented as part of tracking.
Could also add slowing down responses (but of course attacker can send multiple requests that would be handled by different threads).

Some good background: https://security.stackexchange.com/questions/85435/silently-limiting-login-attempts?rq=1
In particular - watch out for telling people an account is locked out since that relays info that the account/username is valid! (send email/SMS instead).

And this one: https://security.stackexchange.com/questions/74211/what-is-the-difference-between-login-throttling-and-temporary-account-lockout

NIST 5.2.2 - Unless otherwise specified in the description of a given authenticator, the verifier SHALL limit consecutive failed authentication attempts on a single account to no more than 100.
and then... When the subscriber successfully authenticates, the verifier SHOULD disregard any previous failed attempts for that user from the same IP address.

What is confusing in the NIST verbiage is the first sentence talks about 'account' the second talks about IP addresses.

@TaaviE
Copy link
Contributor

TaaviE commented Jan 12, 2020

IMHO this should be primarily implemented by things like OSSEC and fail2ban, those tools can also be more effective at deploying countermeasures. Providing a paragraph in documentation that one of them should be set up would be quite good already.

@callejerog
Copy link

callejerog commented Feb 7, 2020

I solved this way:

In my user_model:
wrong_pwd_counter = db.Column(db.Integer)
and
`
class CustomLoginForm(LoginForm):

def validate(self):
    response = True
    if not super(LoginForm, self).validate():
        response = False
    self.user = _security.datastore.get_user(self.email.data)
    if response and not self.user.is_active:
        self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
        response =  False
    if response and self.user is None:
        self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
        hash_password(self.password.data)
        response = False
    if response and not self.user.password:
        self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
        hash_password(self.password.data)
        response = False
    if response and not self.user.verify_and_update_password(self.password.data):
        self.password.errors.append(get_message("INVALID_PASSWORD")[0])
        response = False
    if response and requires_confirmation(self.user):
        self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
        response = False
    if not response and 'password' in self.errors.keys():
        self.user.wrong_pwd_counter += 1
        if self.user.wrong_pwd_counter >= Config.max_wrong_pwd:
            self.user.active = False
            notify_account_locked(self.user.email)
        self.user.save()
    if response:
        self.user.wrong_pwd_counter = 0
        self.user.save()
        if not self.user.can_login:
            self.password.errors.append('Cannot access. Call assistance')
            response = False
    return response

`

@illume
Copy link

illume commented Feb 8, 2020

Would be good if it had something for this enabled by default.

I solved this partially with flask limit.

Also using real time black hole lists, and a CDN in front that has it's own security lists/WAF, and such... But still they get through. They are very, very sneaky (hundreds of proxy IPs, and using up all the attempts per ip for example).

However, it depends on your user base. If your organization has 1000s of people on one ip address, then these blocks fail. With industrial NAT becoming more common in some parts of the world and with ipv4s having run out - this will only get worse. Still, it's probably worth enabling something by default.

(ps. thanks lots for maintaining this!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants