preface

When you learn to write programs, the first line of code is hello World. But when you start learning about WEB backend technology, many people’s first feature is written login (whisper: I don’t know anyone else, but I am). However, when I interviewed or communicated with many students with short work experience, I found that although many students wrote on their resumes that they were responsible for the development and design of the login/registration function module of the project, they simply realized the function logic without giving much consideration to security. The purpose of this article is to talk about what we need to consider when designing a login interface, not only in terms of functional implementation, but also in terms of security.

Security risks

Brute force cracking!

As long as the website is exposed in the public network, so large probability will be targeted, try to blow up this simple and effective way: after obtaining the user name of the website through various ways, through writing a program to go through all possible passwords, until the correct password is found

The pseudocode is as follows:

# password dictionary

password_dict = []

# login interface

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)

Copy the code

So how do we prevent that?

Verification code

Smart students think of it, I can reach a certain number of password error, increase the verification code check! For example, when the user’s password error reaches three times, the user needs to enter the picture verification code to continue the login operation:

The pseudocode is as follows:

fail_count = get_from_redis(fail_username)

if fail_count >= 3:

 if captcha is None:

  return error('Captcha required')

    check_captcha(captcha)

success = do_login(username, password)

if not success:

 set_redis(fail_username, fail_count + 1)

Copy the code

Pseudo code does not consider concurrency, real development can consider locking.

This does filter out some illegal attacks, but with current OCR technology, ordinary image captchas are not very effective at preventing bots (and we’re at a disadvantage here). Of course, we can also spend money to buy a verification scheme like sliding verification provided by the third party company, but it is not 100% secure and can be cracked as well (painful lesson).

The login limit

At that time, some students said, I can directly limit the login operation of abnormal users, when its password error reaches a certain number of times, directly reject the user’s login, and then recover after a period of time. For example, if the number of login errors of an account reaches 10, all login operations of the account will be rejected within 5 minutes.

The pseudocode is as follows:

fail_count = get_from_redis(fail_username)

locked = get_from_redis(lock_username)



if locked:

 return error('Login denied')

if fail_count >= 3:

 if captcha is None:

  return error('Captcha required')

    check_captcha(captcha) 

success = do_login(username, password)

if not success:

 set_redis(fail_username, fail_count + 1)

    if fail_count + 1> =10:

     # Set the lock flag if you fail more than 10 times

     set_redis(lock_username, true, 300s)

Copy the code

Umm, this does solve the problem of user passwords being burst. However, this will bring another risk: although the attacker can not get the website user information, but it can make our website all users can not log in! All an attacker needs to do is loop through all the user names (or even if they don’t exist, randomly) to log in, and those users will be locked forever, preventing normal users from logging in!

IP restrictions

If it’s not possible to target the user name directly, we can target the IP address and seal the attacker’s IP address. You can set a certain IP address to disable login operations when the number of login interface failures reaches a certain number.

The pseudocode is as follows:

ip = request['IP']

fail_count = get_from_redis(fail_ip)

if fail_count > 10:

 return error('Login denied')

# Other logic

# do something()

success = do_login(username, password)

if not success:

 set_redis(fail_ip, true, 300s)

Copy the code

In this way, the problem can be solved to some extent. In fact, many traffic limiting operations are carried out for IP addresses. For example, niginx’s traffic limiting module can limit the number of accesses per IP address in a unit time. But here’s the problem:

  • For example, many schools and companies use the same export IP address. If you restrict users by IP address, other normal users may be killed by mistake
  • With so many VPNS, attackers can switch VPNS to attack after IP is blocked

Phone verification

Isn’t there a better way to prevent it? B: of course. We can see that in recent years, almost all applications allow users to bind their mobile phones. One is required by the national real-name system policy, and the other is that mobile phones are basically the same as id cards, which can basically represent a person’s identity. So a lot of security operations are based on mobile phone authentication, login can also be done.

  1. If the user enters the password more than three times, the user is required to enter a verification code (It is best to use sliding validation)
  2. If the user enters the password for more than 10 times, the mobile phone authentication is displayed, and the user needs to use the mobile phone verification code and password to log in

Mobile verification code brush protection is another issue, I will not expand on it here, later we will talk about what we have done in the area of verification code brush protection.

The pseudocode is as follows:

fail_count = get_from_redis(fail_username)



if fail_count > 3:

 if captcha is None:

  return error('Captcha required')

    check_captcha(captcha) 

    

if fail_count > 10:

 Use the verification code and password to log in more than 10 times

 if dynamic_code is None:

     return error('Please enter your mobile verification code')

    if not validate_dynamic_code(username, dynamic_code):

     delete_dynamic_code(username)

     return error('Mobile phone verification code error')



 success = do_login(username, password, dynamic_code)

    

 if not success:

     set_redis(fail_username, fail_count + 1)

Copy the code

We combine the above methods with the verification mode of mobile phone verification code, which can basically prevent a considerable number of malicious attackers. But no system is absolutely secure, we can only increase the cost of the attack as much as possible. You can choose the right strategy according to the actual situation of your website.

Man-in-the-middle attack?

What is a man-in-the-middle attack

Man-in-the-middle attack, abbreviated to MITM *** : During the communication between A and B, the attacker obtains or modifies the communication between A and B by means of sniffing, intercepting, etc.

For example: small white to small yellow express, on the way through the express point A, black is hiding in the express point A, or simply open A express point B to pretend to be A express point. Then secretly opened xiao Bai to Xiao Huang’s express, see what things there. You can even leave xiaobai’s package and send it to Xiaobai by yourself.

In the login process, if the attacker sniffs the login request sent from the client to the server, it can easily obtain the user name and password.

HTTPS

The simplest and most effective way to prevent man-in-the-middle attacks is to change HTTPS to force HTTPS for all HTTP requests on a website.

*** Why does HTTPS protect against man-in-the-middle attacks? *** HTTPS is the SSL/TLS protocol added between HTTP and TCP to ensure secure data transmission. Compared with HTTP, HTTPS has the following characteristics:

  • Content encryption
  • Data integrity
  • The authentication

The specific PRINCIPLE of HTTPS is not extended here, you can Google

The encrypted

In addition to HTTPS, we can manually encrypt the transfer of sensitive data:

  • User names can be asymmetric encrypted on the client side and decrypted on the server side
  • The password can be transmitted after MD5 is performed on the client to prevent plaintext password exposure

other

In addition to the above, there are many other jobs to consider, such as:

  • Operation logs. Logs (including IP addresses and devices) are recorded for each user login and sensitive operations.
  • Abnormal operations or login reminders. With the above operation logs, we can make risk reminders based on logs. For example, users can be reminded by SMS when they log in abnormally, change their passwords, or log in abnormally
  • Reject weak password When registering or changing a password, a user cannot set a weak password
  • Prevent the user name from being traversedWhen registering, some websites will prompt you if the user name exists after entering it. There is a risk that all the usernames of the site will be exposed (You can iterate over the interface), which requires restrictions on interaction or logic
  • .

Afterword.

Now the country is constantly issued a variety of laws, more and more attention to user data. As developers, we also need to do more to protect user data and user privacy. I will also talk to you later about what we have done in terms of data security, hoping to give you a little bit of help.

Related article

Is your SMS interface really secure?

Further reading

HTTPS – Wikipedia Man-in-the-middle Attack-Wikipedia

[key] reprint please indicate the source: https://juejin.cn/post/6859214952704999438

– END –