In my previous post we looked at how an attacker might circumvent username/password based systems and saw there were several possibilities for attacking these.
In this article I want to look at this scenario from the perspective of the defender and look at what we can put in place to circumvent some of these attack methods.
Do you really want to implement this stuff yourself?
Before we look at implementing this ourselves I want to start off by saying that in many circumstances you are going to be much better off using an existing identity service such as Auth0 or Azure B2C (and there are many other options) rather than handling this yourself.
Companies such as Auth0 and Microsoft put a heap of work into creating very secure and feature rich implementations that have been tried and tested.
Additionally, most of these solutions will come with support for various other features such as MFA (Multi-Factor-Authentication), password reset flows, logging, alerts etc all of which would take a long time to build yourself (and some important features like logging and alerts tend to get forgotten).
These solutions are also pretty cheap at least until you get into having high numbers of users where you can probably afford to pay for them anyway.
However, let’s say for whatever reason you cant use one of these options how can we make our login system as secure as possible?
Multi Factor Authentication
You probably are not going to be too surprised to find this as the number one item.
We can divide the types of authentication into:
- Something we know such as a password or PIN
- Something we have e.g. a Yubikey, RSA token or smart phone MFA App
- Something we are and that is unique like a finger print, retina etc
MFA based authentication simply means using more than one of these items.
Its highly unlikely (or at least very much harder) that an attacker will posses multiple of these items at once.
MFA will likely prevent (or make much harder)
- Brute force/password spray/credential stuffing attacks
- Could alert a user that someone’s trying to log in as them e.g. with an authentication prompt on a mobile device
- Even if an attacker can capture (keylogger) or sniff a login over the network they still will need something else to get in
There are however downsides to consider:
- Additional friction for login – but friction that’s almost certainly worth it for the benefits it provides
- Can be complex for some users and require use of a modern mobile device which may make it unsuitable for some classes of users
- Its hard to implement yourself and you’ll likely end up using a third party service which will incur a cost
- You’ll need a way of resetting this when users lose/forget however you are implementing this
There’s a great blog post by Microsoft’s Alex Wienert (Director of Identity Security) who examines some of the attacks they see on Azure Active directory.
Alex says “based on our studies, your account is more than 99.9% less likely to be compromised if you use MFA.”
Alex also notes that most attackers tried between 10 & 50 passwords before moving on.
Microsoft put together a good paper on advice for password handling that you can read for free.
Some MFA systems can also be configured to block weird behaviour e.g. multiple login attempts from a different location or a login attempt from a location the other side of the world providing additional security protections.
There’s been some discussion around the security of SMS based MFA solutions over the years. Part of this is due to some really rather dumb and trusting processes by telecoms companies to authenticate callers before they port a mobile number (e.g. asking for easily discoverable information). Whilst other non SMS methods are probably more secure most users will have a phone and this means they dont have to install additional software. This makes SMS a pretty good option for some cases.
Disable account after series of invalid logins
Automatically disabling an account after a set number of invalid logins is easy to implement and will almost certainly prevent an attacker brute forcing an account.
Where this gets trickier is that this can become a denial of service vector and allow an attacker to prevent a user accessing their own account!
There’s a few ways you could handle this but probably the easiest is to disable the account for a short period of time and provide a way to enable it again e.g. by time limited email link.
Ensure secure connection
Ensure connections to your login page (and subsequent pages as these will contain authentication related cookies/tokens too!) are made over a secure connection.
Its trivial for an attacker using a tool such as Wireshark or Tcpdump to sniff information being sent over a network such as login details but a secure connection will prevent this.
A certificate will also provide reassurance the user is logging into the correct site – although I’ve seen many organisations use some entirely different domain addresses to their main site which is a bit confusing.
Ensure Passwords are complex but er not too complex
Ensure users set passwords with sufficient complexity to avoid easy brute force attempts.
Perhaps you could even use one of the smaller lists at https://github.com/danielmiessler/SecLists to check the password is not in a top 1000 password list for example.
There’s a balance to having sufficient complexity requirements to make a password hard to guess/brute force crack and encouraging users to find (insecure) ways to remember it!
Forcing rotation of passwords also needs to be balanced against this as most users will just increment a number on the end or something similar.
Don’t restrict characters, length or truncate passwords
Restricting characters, length or truncating a password reduces its complexity and there’s really no good reason (maybe integration with something really old that cant be changed?) to do this.
Store passwords securely
OWASP provide detailed advice on the best way to do this – at the time of writing they suggest the hashing function Argon2id (I hadnt heard of this either) with 2 iterations and 1 degree of parallelism. Note that Argon2id also salts the password to make it harder to crack.
Don’t give anything away with your invalid login messages
Provide the same response for valid/invalid usernames and passwords and if an account is locked out to prevent an attacker using this as an enumeration method. Even subtle changes in rendered HTML like an extra whitespace character could be used to give away details of whether a username is valid or if an account is now locked out.
Logging & Alerts
Whilst there will always be some perfectly legitimate invalid logins where users have forgotten their password or made a typo you’ll probably want to know if there are 100 or 1000 of attempts on an account or a heap of attempts from the same IP address (be aware applications and scripts that will rotate through servers or proxies to avoid this).
Having some form of an alert system can allow you to take a decision on how you want to handle this such as blocking the IP address/network or disabling the account.
Logging is really important as allows you to understand what has happened, where the issues may be and potentially could be important if an attacker gains access to further systems. You may even need it for legal action.
Obviously don’t store your logs in the same place as your solution as guess where an attackers going first if they are successful getting onto your system..
Ensure Users Keep their details up to date
You’ve probably seen that annoying prompt every so often from sites where they check your contact details. Whilst some are probably doing this to spam you it is important these details are up to date so they can contact you and potentially have a way for you to re-enable your account if it is blocked.
Require the user to enter their password to perform a sensitive functions
Privileged operations such as change of email address or password should require additional authentication to prevent say an attacker changing a password or transferring money if a machine has been unlocked or perhaps a CSRF attack.
Make your solution Password Manager friendly
Password managers are a great solution to the problem of having to remember a heap of logins. Some sites disable features such as paste into login boxes or prevent the use of certain characters which can stop password managers working so well. Don’t make your users lives harder these tools make it easy to use complex passwords across sites.
Be wary about using the same authentication systems externally and internally
Whilst it may be very convenient to have an externally facing web application you have developed use your internal networks active directory (and there’s some benefits of centralisation or legitimate use cases) this could very much weaken your network security by providing another (and likely very much weaker point) to attack. E.g. if there’s an issue in your application that it could be potentially used to gain wider access depending on how this is implemented.
Don’t use/allow highly privileged accounts to login in remotely
This is probably veering off the appsec side of things which is where I want to focus but you probably should not connect remotely using highly privileged accounts as this could provide an opportunity for an attacker. Instead have separate privileged accounts that are just used when required.
Be aware of language specific issues
Some languages and frameworks provide special functions to compare password hashes. Note that some languages/frameworks e.g. PHP have some issues you should be aware of such as Magic hashes.
Geographical and time restrictions
Are you users only located in one country?
If so restricting to requests to a specific country could prevent evil bots that crawl the internet discovering your service and make it slightly harder for attackers.
Obviously, there are a heap of ways around this such as VPNs, use servers in another country etc so this should only be used in conjunction with some of the other methods we’ve discussed.
- Don’t build this stuff yourself unless you have to
- Use industry standard protocols such as OAuth
- Use MFA
- Disable accounts after a number of invalid login attempts
- Enforce balanced complexity requirements
- Ensure you have alerting and logging in place
OWASP have a great cheat sheet to implementing authentication: