How to Harden Login APIs: From Brute‑Force to MITM Protection
This article examines common login security risks such as brute‑force attacks, captcha bypass, IP blocking, and man‑in‑the‑middle threats, and proposes layered defenses including password‑retry limits, captcha, phone verification, HTTPS, and encrypted payloads to significantly raise the attack cost.
01 Preface
When learning web backend development, many newcomers focus only on implementing login functionality without considering security aspects. This article discusses what additional security measures should be taken when designing a login interface.
02 Security Risks
Brute‑Force Attacks
Publicly exposed sites are vulnerable to password‑guessing attacks. An attacker can obtain usernames and iterate through possible passwords until a correct one is found.
# password dictionary
password_dict = []
# login endpoint
login_url = ''
def attack(username):
for password in password_dict:
data = {'username': username, 'password': password}
content = requests.post(login_url, data).content.decode('utf-8')
if 'login success' in content:
print('got it! password is : %s' % password)To mitigate this, we can introduce captcha verification after a certain number of failed attempts.
Captcha
fail_count = get_from_redis(fail_username)
if fail_count >= 3:
if captcha is None:
return error('需要验证码')
check_captcha(captcha)
success = do_login(username, password)
if not success:
set_redis(fail_username, fail_count + 1)Note that simple image captchas can be broken by OCR; third‑party sliding captchas are an alternative but not foolproof.
Login Rate Limiting
fail_count = get_from_redis(fail_username)
locked = get_from_redis(lock_username)
if locked:
return error('拒绝登录')
if fail_count >= 3:
if captcha is None:
return error('需要验证码')
check_captcha(captcha)
success = do_login(username, password)
if not success:
set_redis(fail_username, fail_count + 1)
if fail_count + 1 >= 10:
# lock account for 5 minutes
set_redis(lock_username, true, 300)However, locking accounts can be abused by attackers who iterate over many usernames, causing legitimate users to be locked out.
IP Limiting
ip = request['IP']
fail_count = get_from_redis(fail_ip)
if fail_count > 10:
return error('拒绝登录')
# other login logic
success = do_login(username, password)
if not success:
set_redis(fail_ip, true, 300)IP‑based limits may affect multiple users behind the same NAT and can be evaded with VPNs.
Phone Verification
Binding a phone number to an account enables a second factor. After three failed attempts, require a captcha; after ten failed attempts, require a phone verification code in addition to the password.
03 Other Measures
Record operation logs for each login and sensitive action (IP, device, etc.).
Send alert messages (SMS/email) on abnormal logins.
Reject weak passwords during registration or password change.
Avoid exposing username existence checks to prevent enumeration.
04 Postscript
With increasing data protection regulations, developers must continuously improve user data security. Future articles will share additional practices.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
