User authentication is employed by a large number of websites. However, the security of that authentication is often not properly implemented, making the websites an easier target for malicious hackers. This resulted in a thirty-percent increase in web-based attacks in 2013 (according to the Symantec Internet Security Threat Report 2013), compromising more than 93 million identities during the same year.
Having that in mind, it is a good idea to pay more attention to properly securing your website. This article shows you some tips and techniques that will reduce the risk of your website being compromised and make your site a much harder target for attackers.
Eavesdropping and tampering
Eavesdropping can be prevented by using Hypertext Transfer Protocol Secure (HTTPS). HTTPS is a protocol that uses HTTP on top of SSL in order to provide secure communication over a computer network. Unlike HTTP, which sends all the information in plaintext, HTTPS encrypts all the data that is being transmitted. The attacker could only see encrypted text, which is computationally impossible to decrypt even with the strongest available computer today.
SQL injection is a type of attack which occurs when the login form input is not properly filtered, allowing the attacker to alter the SQL query and authenticate himself without knowing the password. Let's assume that you use the following query without escaping or filtering the input:
SELECT USER from database WHERE username='$username' AND password='$password'
Now, let's say the attacker comes to your website and enters the following information in the login form:
username: 'a or 1=1--
The SQL query then becomes:
SELECT USER from database WHERE username='a' or 1=1-- AND password=''
The following query would always evaluate true, because "OR" operator returns true when at least one of the conditions is true, and 1=1 is always true. The command "--" is used for comments, meaning that the rest of the query would be ignored. In this scenario, the attacker would be authenticated and would access restricted areas and data without knowing the correct username/password combination.
This is just one example of SQL injection. Some weaknesses of this type may allow attacker to retrieve a whole SQL table.
SQL injection can be prevented using mysqli prepared statements. Here is a sample code in PHP:
$stmt = $mysqli->prepare("INSERT INTO users (name, age) VALUES (?,?)");
// bind parameters. I'm guessing 'string' & 'integer', but read documentation.
// *now* we can execute
Note that SQL injection prevention should not be done only on the login form, but site-wide.
Although you have implemented HTTPS and secured the login from SQL injection, there are other ways that can lead to your data being available to the attackers — attacks on your hosting provider, someone from your company working for the attackers, etc. Whatever the reason, the security risk must be minimized in this worst-case scenario.
The first and the most important thing is not to store passwords as plain text, as all accounts would be compromised in that case. Store them using a one-way hash function. Hash functions are not reversible, meaning that the attacker would have to hash all possible passwords and then if the two hashes match, he would have found the password. Using Bcrypt for storing passwords is recommended for several reasons. First, salt (a fixed number of random characters) is added to the password, making the attack using rainbow tables (hash lookup tables which increase the speed and effectiveness of a brute-force attack) impossible. Second, Bcrypt is an iterative function — after the password has been hashed, the same process is repeated for a fixed number of times before storing the hash into the database. When a user is logging in, there is a delay that lasts a fraction of a second, but that same delay can significantly slow down a brute-force attack. For example, with 0.008 second delay, the attacker could only test 125 passwords per second, which is 10800000 passwords per day. This is a great result compared to other hash algorithms, which can be tested for hundreds of millions of passwords per second (oclHashcat - advanced password recovery). The number of iterations that you should use depends on your server's CPU strength, sensitivity of your data, etc. This page can help you calculate it.
This type of attack is very simple — it involves trying all possible username/password combinations until the right one is found. Unlike the movies, where the passwords are guessed manually, brute-force attack usually relies on software to make the guessing process faster.
To decrease the risk of a brute-force attack on web login forms, it is recommended to use a strict password policy — users can be required to use a password that is minimum 6 characters, that needs to have mixed case and both letters and numbers, and that they need to change the password after a certain time has passed. Also, some (common and easy passwords should be blocked. Users should be asked a security question upon registration, and if there are three unsuccessful login attempts, block further login attempts until the question has been answered. Also, IP addresses can be blocked after a number of unsuccessful attempts, but be careful with this, as you could accidentally block ISPs or company's proxy server.
Since the attackers usually use software for brute-force attack, there are some techniques that can confuse their computer. A good solution is not to use predictable behavior for failed passwords. For example, most Web sites return an "HTTP 401 error" code with a password failure, although some web sites instead return an "HTTP 200 SUCCESS" code but direct the user to a page explaining the failed password attempt. This fools some automated systems, but it is also easy to circumvent. A better solution might be to vary the behavior enough to eventually discourage all but the most dedicated hacker. You could, for example, use different error messages each time or sometimes let a user through to a page and then prompt him again for a password.
Session keys and session cookies are used to pass authentication information between the pages of a website and remember users' login information. Session hijacking refers to stealing this key and obtaining unauthorized access to the restricted area without knowing the username/password combination. There are several types of session hijacking attacks.
A user's session can be stolen by intercepting the communication between the user and the website's server and extracting session key from it. This is called session sniffing and can be prevented by using encryption, i.e. using HTTPS.
If the session key is not long enough, an attacker could guess the session key by trying all possible combinations. Use a long random number or string as the session key.
Other good practices to prevent session hijacking include: regenerating session key after successful login, secondary user identity checks (does the current IP address and browser match the ones used during the last session), and regenerating session keys with each request or every 15 minutes.
Cross-site request forgery (CSRF)
CSRF is an attack which forces an end user to execute unwanted actions on a web application in which he/she is currently authenticated. With a little help of social engineering (like sending a link via email/chat), an attacker may trick the users of a web application into executing actions of the attacker's choosing. A successful CSRF exploit can compromise end user data and operation in the case of a normal user. If the targeted end user is the administrator account, this can compromise the entire web application.
The most effective CSRF counter-measure is to use a random token that is associated with the user's current session whenever the user wants to execute an operation on the site. The token would be included in the HTML form:
<form action="/transfer.php" method="post">
<input type="hidden" name="CSRFToken" value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZjMTVi
Whenever a request to transfer.php is submitted, the first thing to be checked is if there is a CSRFToken and if that token is valid.
Websites that need strict security should avoid the "remember me" option and should automatically log users out after a certain time of inactivity (usually 15 minutes). Also, they should ask a user to re-enter a password when a sensitive operation is invoked (e.g. money transfer, settings change, etc.).
Secure web login cheat sheet
✔ Use HTTPS with an up-to-date version of SSL
✔ Prevent MySQL injection on whole website by using mysqli prepared statements and binding
✔ Store passwords as one-way hash function. Use bcrypt for this
✔ Use both dynamic (different for each user) and static salt values when generating a password hash
✔ Enforce minimum password length — 6 or 8 characters
✔ Enforce using mixed case letters and numbers in passwords
✔ Require password change every 6 months
✔ Disallow using most common passwords as a password
✔ After 3 unsuccessful login attempts, ask the secret question (which user choose upon registration) and disallow further login attempts until the question is answered
✔ Consider blocking IP addresses after a certain number of unsuccessful login attempts
✔ Use unpredictable behavior for failed passwords to fool automated brute-force tools ✔ Use long random number or string as the session key
✔ Filter input properly in order to prevent XSS (cross-site scripting) vulnerabilities
✔ Regenerate session key after the user has logged in
✔ Use a random token that is associated with user's current session whenever a user wants to execute an operation on the site