DEV Community

Cover image for How not to build a login & signup system
Jude Eigbiremonlen
Jude Eigbiremonlen

Posted on

How not to build a login & signup system

You shouldn't build a login & signup system that only securely and effectively authenticates users; instead, create a system that makes sure your application does more than just securely authenticate users by adding an extra layer of security, which includes the following:

  1. Create a login attempt tracker
  2. Always add a CAPTCHA to your signup/registration form.

Yeah, this is information that you may get from a senior dev or probably too late after your system or user has been exploited. Note these concepts can be implemented with any programming language.

So let's explain the reasons and how to properly implement these extra layers.

Reason

The reason why you should always add CAPTCHA to your signup/registration form is to prevent automated systems or bots from flooding your servers and creating fake accounts.

The login page doesn't necessarily always require a captcha because it is not a good user experience to always require an already existing user to answer the captcha every time they login.

Instead, you create a system that tracks user login attempts and shows the CAPTCHA when they reach a certain number of failed login attempts.

This will prevent unauthorized individuals from guessing your user's login credentials and also protect your server from potential brute-force attacks.

How to properly implement the login attempt tracker security layer

You need to create a database table to store login attempt details, increase the counts every time and create an httpOnly cookie "attemptCounts" for server-side access only. The main reason to save the details to a database and not cookies only is to prevent attackers from altering the data and also to be able to identify and track clients even if they switch devices, locations or IP addresses.

So you can either identify & track each attempt by a user email/IP/username/phone-number using any of these single options or combine them based on the data you collect.

Example of a simple login attempt table:

id: serial
email: string //email or username or phonenumber
ip: string
counts: integer
createdAt: timeStamp
updatedAt: timeStamp
Enter fullscreen mode Exit fullscreen mode

The maximum failed login attack should be 5. Some enterprise and financial systems may set it to only 3 attempts; between 3 and 5 works fine.

The next thing to do if they exceed the allowed login attempts:

1. Implement the CAPTCHA mechanism: Show the captcha on the login form and make it compulsory every time until the user login successfully and then update the login attempt to 0 in the db.

2. Implement time-lockout mechanism: Prevent the client from being able to submit the login form for a specific timeframe.

3. Block the account and force a password reset: Send a password reset to the user's email if it exists, or the user has to visit your office physically. This option is mostly used by banking apps & fintech.

You can implement any of them or combine them as you like. Some applications use all three, while most apps implement only captcha or time-lockout mechanism.

Make sure you track IP address and username/email/phone-number so that even if they try from another device or IP, your system is still able to detect and identify them.

Ok, that will be all for now.

Top comments (2)

Collapse
 
circuit profile image
Rahul S

One subtle gotcha with tracking attempts per email — if the CAPTCHA trigger or lockout behaves differently for "email exists, wrong password" vs "email doesn't exist at all," you've accidentally built an account enumeration oracle. An attacker tries 5 logins against test@example.com, gets CAPTCHA → that email has an account. Tries 5 against fake@example.com, gets a different response → no account. Now they've got a verified list of active users without ever guessing a single password. The fix is making the response path identical regardless of whether the email exists in your system — same delay, same CAPTCHA trigger, same error message. Sounds obvious but it's really easy to accidentally leak through timing differences too (DB lookup for existing user takes longer than an early return for nonexistent one).

Collapse
 
juddee profile image
Jude Eigbiremonlen • Edited

Yeah, the system doesn't check if an account email/username exists or not.
Like I mentioned, you only track login attempts regardless of if an actual user has the email or username details being submitted.

This is the same principle used by banking and enterprise software; it doesn't check if the details like the email or username actually exist as a user in their record.

Your system is only tracking and monitoring the login attempts based on your set unique identifiers like IP address, email, username, etc.

So it doesn't take any extra time between an existing user and a non-existing user because that is not being checked at all by the system.