Good practices for user authentication in web applications
User authentication is a common concern for almost every web application. If it is not implemented correctly or follows good practices and standards, it can make the web application vulnerable to various attacks, whose sole purpose is to hijack the user session or leak sensitive data.
Do not reveal user accounts
A common issue with web forms for recovering user password, or registering new users is that they can reveal if an user account already exists in the system. That represents the first line an attacker can use to determine first if the user account exists, and then try to infer the password with brute force or social engineering attacks.
For the forgot password use case, do not return an error saying the account does not exists if the entered account is not registered in the system. Simply return a generic message such as "The link to recover the password has been sent to the registered email". In that way, an attacker can not find out if the account really exists or not.
For the user registration scenario, try to use a captcha as part of the registration form. That will prevent someone to run the registration form in an automated fashion or using scripts to determine whether the account exists.
Provide multi factor authentication
Multi Factor Authentication (MFA) or also know as Two Factor Authentication refers to a technique that requires an additional authentication factor in addition to a password to gain access to a system. Factor one is something that the user knows, a password, and factor two is something the user owns, such as a unique code sent to a mobile device with an application or SMS or a proprietary device (e.g an RSA token). If the password is compromised, an attacker will still need the second factor to authenticate.
As proprietary devices (a.k.a hard tokens) are expensive and requires logistics for distribution, second factor is usually implemented through mobile apps that deliver a unique code or token to the user (a.k.a soft tokens). Some examples of mobile apps are Google Authenticator or Auth0 Guardian app.
In the past, second factor was also implemented with SMS, but it was proven after many attacks that SMS is not a reliable solution and it can be impersonated.
Provide WebAuthN support
WebAuthN is the name of a new protocol to support passwordless authentication in web browsers. The protocol relies on a public key cryptography scheme to generate a private key that is stored somewhere in a safe place on the client side, and a public key that is shared with the server. Authentication happens in a form of a handshake between the browser and the server. The browser signs a message with the private key and the server verifies that message with the public key. If the message verification is successful, the user is authenticated. Both the browser and the server must support an API to implement this protocol. As you can see, this a solution for more sophisticated users.
Fido2 is another specification that relies on WebAuthN to store the private keys in devices protected with a password or PIN. It is another way to support two factor authentication. In order to authenticate in a web application, the user not only needs to have the device but also the PIN or password to unlock the private key on it. Private keys are stored securely on the device and it can not be exported, so it also provides an extra layer of security.
Limit login attempts
Make sure your authentication form is not susceptible to brute force attacks. Implement a policy to prevent this from happening. That policy can take multiple shapes,
- Block the account after multiple attempts, and notify the user.
- Add a delay that prevents user from being authenticated for a period of time
- Add a captcha for preventing any script for performing the attack in an automated fashion.
If you are implementing authentication through an HTML form, you can also use leverage the CSRF protection mechanism that most web frameworks provide. That will prevent anyone to try submitting the form from an scripting tool.
If you are implementing authentication through API calls from JavaScript, make sure to use API throttling and the same-origin policy enabled. The former will limit the number of calls that can be made to the API in a given period of time by checking the source IP address. The later will prevent the call to be made from an scripting tool.
Use third party authentication
As last option, if you don't want to deal with all the issues listed above, you can delegate authentication to a third party service like Auth0, Okta or Azure AD.
Those services act as identity brokers that implements the Federated Identity pattern.
You configure your web application to trust any authentication token coming from them. They take care of authenticating users using best practices and standards for authentication. These usually support a big variety of authentication or identity providers, so you can easily add support for those in your application with simple configuration.