Login Page for Modern Applications
This short note might not align with someone's opinion or the reality at the time of reading I am simply trying to find the right design that doesn't annoy users while ensuring security for online applications Often, I see online resources accepting a login-password pair at once without requiring any second factor For a book store, this might be acceptable But for a more serious application, it's not great because this approach is vulnerable to brute-force attacks, even though it's convenient for users Below, I will discuss security measures and their user-friendliness CAPTCHA! CAPTCHA is an effective way to stop most unprepared attackers from brute-forcing passwords. But let's be honest — it directly impacts users, and some CAPTCHAs are outright annoying Classic CAPTCHA (entering symbols from an image): the least convenient “Selecting traffic lights”: slightly better but still frustrating Google Invisible reCAPTCHA: convenient for users as it requires no action, but it's absolute — users have almost no chance to prove they're not robots Cloudflare Turnstile: similar to invisible reCAPTCHA but may show a checkbox to click if necessary The last approach is less irritating, but even here there's room for optimization: For example, if your login form is used on average once per minute during regular working hours, you can enable CAPTCHA only when usage exceeds twice or ten times the average rate This way, CAPTCHA activates automatically during brute-force attacks and stops bots, while regular users are affected only during attacks Remember site-specific busy days (e.g., Black Friday, Christmas), so apply this approach thoughtfully Split Data Entry! Most popular platforms today use step-by-step data entry. For example: Step 1: The website sends the user's login to /api/auth/login The site creates a temporary token Saves it for a few seconds Sends it back to the client This step works regardless of whether the user exists in the system Step 2: The app sends this token and the user's password to /api/auth/pwd The endpoint retrieves the login from the cache using the token Processes it as a login-password pair The temporary token can be anything: a cookie, JWT, encrypted value, or signed string — not necessarily cached The key point: splitting login and password entry into two calls with an intermediate token stops most brute-force tools, example: CSRF Tokens The previous point can be partly solved using CSRF tokens It's enough to study your framework and learn how to turn on this feature. It can also help against brute-force attacks. But I want to note that many tools for attacking web applications can work with many standard CSRF token systems In any case, they will be useful for the whole application, not just for login Plus, they will reduce the number of simple attacks Add Delays If an attacker bypasses CAPTCHA and can easily implement sequential API calls for login and password, a good method to create difficulties for the attacker is adding a random delay to one of the steps Firstly, this slows down the attack process Secondly, it protects against user enumeration attacks by reducing the likelihood of the attacker identifying which usernames exist in the system based on API response delays Key considerations: Delays should not allow the attacker to cause system denial They should not frustrate users, so the faster the API works, the better This protection can be activated only when normal metrics are exceeded, i.e. during an actual attack. It is important to implement this automatically rather than manually Balance is crucial: If the user exists, the service may perform additional operations such as reading data from the database, adding 200-300 milliseconds of delay If the user does not exist and the API responds as quickly as possible, a random delay of 150-250 milliseconds should be added to prevent detection of whether a user exists Ensure you test your application’s behavior if 10,000 such requests arrive simultaneously Rate Limits It is crucial to limit the number of authentication method calls per IP For example, the following metrics can be considered normal: one user logs into their profile an average of 5 times a day. Even if it's a group of 100 users from the same office (same IP), 500 logins over 12 hours from one IP address are considered normal, even if they all decide to log in at the same time However, in this scenario, calling the authentication method 1000 times per minute would appear as a significant anomaly Thus, you can respond only when a specific limit is exceeded for a particular IP address Keep in mind that today attackers may use multiple IP addresses Therefore, it is worth monitoring overall login metrics as well, similar to how CAPTCHA was discussed earlier Possible actions when suspecting an attack: Require CAPTCHA for the suspicious IP Introduce delays Block attackers (HTTP 429

This short note might not align with someone's opinion or the reality at the time of reading
I am simply trying to find the right design that doesn't annoy users while ensuring security for online applications
Often, I see online resources accepting a login-password pair at once without requiring any second factor
For a book store, this might be acceptable
But for a more serious application, it's not great because this approach is vulnerable to brute-force attacks, even though it's convenient for users
Below, I will discuss security measures and their user-friendliness
CAPTCHA!
CAPTCHA is an effective way to stop most unprepared attackers from brute-forcing passwords.
But let's be honest — it directly impacts users, and some CAPTCHAs are outright annoying
- Classic CAPTCHA (entering symbols from an image): the least convenient
- “Selecting traffic lights”: slightly better but still frustrating
- Google Invisible reCAPTCHA: convenient for users as it requires no action, but it's absolute — users have almost no chance to prove they're not robots
- Cloudflare Turnstile: similar to invisible reCAPTCHA but may show a checkbox to click if necessary
The last approach is less irritating, but even here there's room for optimization:
For example, if your login form is used on average once per minute during regular working hours, you can enable CAPTCHA only when usage exceeds twice or ten times the average rate
This way, CAPTCHA activates automatically during brute-force attacks and stops bots, while regular users are affected only during attacks
Remember site-specific busy days (e.g., Black Friday, Christmas), so apply this approach thoughtfully
Split Data Entry!
Most popular platforms today use step-by-step data entry.
For example:
Step 1: The website sends the user's login to /api/auth/login
- The site creates a temporary token
- Saves it for a few seconds
- Sends it back to the client
- This step works regardless of whether the user exists in the system
Step 2: The app sends this token and the user's password to /api/auth/pwd
- The endpoint retrieves the login from the cache using the token
- Processes it as a login-password pair
The temporary token can be anything: a cookie, JWT, encrypted value, or signed string — not necessarily cached
The key point: splitting login and password entry into two calls with an intermediate token stops most brute-force tools, example:
CSRF Tokens
The previous point can be partly solved using CSRF tokens
It's enough to study your framework and learn how to turn on this feature. It can also help against brute-force attacks. But I want to note that many tools for attacking web applications can work with many standard CSRF token systems
In any case, they will be useful for the whole application, not just for login
Plus, they will reduce the number of simple attacks
Add Delays
If an attacker bypasses CAPTCHA and can easily implement sequential API calls for login and password, a good method to create difficulties for the attacker is adding a random delay to one of the steps
Firstly, this slows down the attack process
Secondly, it protects against user enumeration attacks by reducing the likelihood of the attacker identifying which usernames exist in the system based on API response delays
Key considerations:
- Delays should not allow the attacker to cause system denial
- They should not frustrate users, so the faster the API works, the better
This protection can be activated only when normal metrics are exceeded, i.e. during an actual attack. It is important to implement this automatically rather than manually
Balance is crucial:
- If the user exists, the service may perform additional operations such as reading data from the database, adding 200-300 milliseconds of delay
- If the user does not exist and the API responds as quickly as possible, a random delay of 150-250 milliseconds should be added to prevent detection of whether a user exists
- Ensure you test your application’s behavior if 10,000 such requests arrive simultaneously
Rate Limits
It is crucial to limit the number of authentication method calls per IP
For example, the following metrics can be considered normal: one user logs into their profile an average of 5 times a day. Even if it's a group of 100 users from the same office (same IP), 500 logins over 12 hours from one IP address are considered normal, even if they all decide to log in at the same time
However, in this scenario, calling the authentication method 1000 times per minute would appear as a significant anomaly
Thus, you can respond only when a specific limit is exceeded for a particular IP address
Keep in mind that today attackers may use multiple IP addresses
Therefore, it is worth monitoring overall login metrics as well, similar to how CAPTCHA was discussed earlier
Possible actions when suspecting an attack:
- Require CAPTCHA for the suspicious IP
- Introduce delays
- Block attackers (HTTP 429)
- Block and respond with a message claiming your Ruby server cannot process the request, even if your server is actually written in Go