Login pages – they’re everywhere. Almost every website has a login page – from big companies to discussion forums.
In this blog post I’ll try to explain how login pages work and also show how to create one, but instead of spoonfeeding information, I will only explain the core concept. I will also clarify how to mitigate two types of attacks that can occur if your website has a login functionality. Let’s begin, shall we?
A basic login form has three input fields – one for a username or an email address, one for a password and one for a login button.
My login form (shown below) has four of them – one of the fields is used to mitigate an attack that is often overlooked.
The majority of login pages have one inherent weakness that is Cross-Site Request Forgery. I am not going to cover what it is as I already did that in a previous blog post but rather I will try to explain how it can be mitigated on a login page so your users won’t be able to log other users (or yourself) in without permission.
Login Cross-Site Request Forgery is such an attack that may occur on a login form.
Since the majority of login pages do not protect against this type of attack, it is easy for an attacker to log a user in by knowing the appropriate POST parameters – the browser would send a POST request to the page containing the login credentials and if they are correct, the user would be logged in.
My login form has 6 files:
I’ll start from the top and move towards the bottom, but before I start I would like to highlight one critical point: You should be submitting and delivering the form over HTTPS because if your form isn’t delivered over a secure channel, passwords typed in the form could be stolen by an attacker. More information on HTTPS can be found here.
The configuration file should contain the host name, which is used to connect to your server, your database user name, your database user password and a database name.
When you create a variable that connects to your database and successfully connect to it, an instance of the PDO (assuming you’re using it) class will be returned to your script. To destroy the connection, simply assign NULL to the variable that holds the object.
The index should contain four input fields – one for a username or an email address, one for a password, one for a hidden CSRF token and one for a login button. Name your fields accordingly, but leave the CSRF token’s value blank for now – we’ll come back to it later.
Your database should have a field for a username or an email address, two fields for a password hash and its salt (a salt is random data that is used as an additional input to a hash), one field for login attempts that will be reset upon successful login, one “lockout” field where you will check whether the user is locked out of the form or not and one field for defining the lockout period.
Also if you’re considering storing passwords in plain text, please don’t – if you do so, the security of your service could be very easily compromised. Hash the passwords instead – hashing is the act of converting passwords into unreadable strings of characters. Since some hashing functions are more resilent to cracking than others, I’d recommend you use BCrypt.
After you’ve designed your input fields and database structure, you need to do one more thing – you need to create a process that happens after you click the “Log In” button:
The login lockout file should update the database with a query to increment the login attempts each time a user is attempting to log in.
If the user attempts to log in more than 5 times and fails but is not locked out, set the lockout value in the database to “1” or “Locked Out” – this prevents bruteforce attacks. You should also set the lockout time to the current time plus the amount of minutes you want the user to be locked out. In my login form the login lockout time is set to the current time plus 5 minutes. That’s five minutes of lockout. Yours might be different.
The file should also check whether the user is attempting to login while the lockout value is set to “1”. If this is the case, reset the login attempts, throw an error and forbid logging in until his lockout time has expired.
A cross-site request forgery attack can be prevented by generating tokens. A properly generated token should be:
If the token in the hidden field matches with the token in a session, unset the session token and let the user log in. If it does not, block the request. In my case, the token is added as a hidden field value within the login form. It can also be added as a GET parameter.
After you have started a session and redirected the user to the main page, you can greet him by returning “Hi, ” and his session name. From this point, you can do multiple other things too – that may be a topic for another day though.
Logging the user out is rather simple – destroy his session and redirect the user back to the index.
To summarize:
A demo version of my login form is available here.
This blog will walk you through some lessons for conference speakers who speak remotely no…
Insecure data storage is the second vulnerability in the OWASP Mobile Top 10 list. Insecure…
The improper platform usage vulnerability is the first vulnerability in the OWASP Mobile Top 10.…
If you ever heard of web application security, you probably heard of OWASP. And if…
If you have ever built a website, chances are you took care of security. Securing…
It's November. For some developers it's just an ordinary month - for some of them…