Nowadays, if you’re building something that requires users to be able to login, you’re immediately faced with the issue of making it both secure and flexible. With computers getting faster, it’s much easier to crack passwords through brute force. In addition to this, there is also the possibility of session hijacking. There are some options out there for dealing with these issues to make your login system more secure, but most of them are inflexible. If you want to, for example, build a mobile app that uses the same secure login system, a lot of implementations wouldn’t easily port over, or would have to be reimplemented in a different way.
Here is an easy way to build a login system that is both flexible and secure.
1. Always use HTTPS
If you’re transmitting passwords or cookies over HTTP, anyone on the same network can intercept that traffic and see your password or your cookies, and they can hijack your login session. Forcing HTTPS for all requests takes care of this, and it can be done through your Apache or Nginx configuration files.
2. Only send form data over POST, not GET
There are 2 primary ways to send form data, such as the email and password entered in a login form: POST and GET. GET requests put the data in the url so it looks something like http://email@example.com&password=yourpassword. Even if you are using HTTPS, the URL can be visible in your browser history, and even worse than that, other requests (such as loading google analytics or even going to another website) will carry an HTTP referrer header. This will pass the url http://firstname.lastname@example.org&password=yourpassword to those other websites in the HTTP headers. Always use POST instead of GET. This puts the data within the request itself, which, as long as you’re using HTTPS, is encrypted and not visible to anyone.
3. Encrypt passwords
This should go without saying. If somehow anyone were to ever get their hands on your user database, you wouldn’t want your users’ passwords exposed in plain text. Hashing algorithms can created hashes of their passwords, but you want to take it one step further to prevent brute force dictionary attacks. Instead of MD5, opt for something more secure like SHA256. And on top of that, run the hash through the hashing algorithm a hundred or a thousand times. This may be a little bit slow when a user registers or logs in, but you won’t be running the hashing algorithm every time they load a page, so some small delays can be okay when they login.
4. Track login attempts
This is the easiest way to prevent brute force attacks. If you’re tracking login attempts, you can set it to lock an account if there are multiple unsuccessful login attempts in a very short period of time. Then just automatically send the user an email with a link to reset their password.
5. Store active sessions in a database
This is where the flexibility comes in. When a user logs in, create a random key consisting of letters, numbers, and symbols. Store this on your server, in a key-value store, such as Memcached, Cassandra, Apache Hbase, or a 2 column MySQL table. The key will be something like “login-yourrandomkeyhere” and the value will be the userID. Then send the key (the randomly generated string) to the user over HTTPS. The flexibility comes with this key. It can be stored as a cookie in a browser, or as a login key in a mobile app, etc. Every time the browser, app, etc. makes a request to your server, it just includes the key in the body of the request, and the server is able to verify that the user is logged in, and deliver the appropriate content. And here’s the beautiful part: if a user ever forgets to logout on a certain device, you can give them the ability to remotely logout. You just revoke the session by removing that key from the key-value store.
Hopefully this gives you some ideas, and you can take one more step in making your website more secure.