— This post is part of a series of monthly blog posts about all kinds of Security topics for Developers —
The HTTP protocol is stateless, meaning that the server is not required to store state information for a conversation. This simplifies the protocol a lot, but there are often situations where keeping state is desired. For example, you don’t want users to send their login credentials with each request. To prevent having to reauthenticate all the time, sessions were invented. Sessions are great because they allow the user to authenticate once, and then stop thinking about it. However, hackers also find them great, because if sessions leak, the hacker doesn’t need to authenticate either to use your account. So, how do we do proper session management and prevent session data from falling into the wrong hands?
The session ID
Sessions are usually identified by a session ID. That session ID can be used in a request to prevent having to reauthenticate. Authentication is like having to show your passport to the security guy at the front door to prove that you are who you are. If you are allowed in, you get a session, which is like a key. Next time you can bypass the security guy and just open the door yourself. It is a proof that you have already authenticated recently. Like with all keys, you don’t want to loose them, or other people can impersonate you and do bad stuff.
As for the ID itself, that should obviously be random, unique and long. It should in no way be dependent or related to any previous session ID that was issued by the server. If session IDs are not unique, users may unexpectedly see data from each other. If the ID is not random, hackers may learn the algorithm, and calculate what new session IDs will be issued. And if they are too short, hackers can just try ‘all’ of them until they find a valid session. Please don’t try to be smart and calculate new session IDs based on your own genius algorithm, but learn from all the mistakes other developers made in the past when trying this.
One of the most important aspects about securing sessions, is that the session ID may not leak. This means that we should think about how to protect it when it is stored (both on the client and the server) and especially when it’s in transit. Use browser cookies to store and send the session ID to prevent session fixation.
If you allow session IDs to be communicated by URL parameters, a hacker may send you a link to https://example.com/login?sessionid=abcdef123456. When you login, the authentication is linked to a session that is also known by the hacker. He can now take over your account. To prevent this, always use browser cookies to store the session.
Prevent leaking cookie data
Also set the Secure flag, to prevent the session cookie from ever being sent over an insecure connection. Cookies sent over an insecure connections can be read by anyone who’s on the same line. Needless to say, your website itself should also run solely on HTTPS. Otherwise the server could still send the Set-Cookie header for the session ID over an unencrypted connection.
Prevent using session IDs for other purposes
Using session IDs for anything else then identifying the session is an absolute no-go! The more you use the session ID in different parts of your code, the more likely that at some point you (or one of your fellow developers) makes a mistake, and the session ID leaks to other people.
Ideally your application has an authentication layer where all requests pass through. After checking session data and verifying that all is well, you can remove the session cookie from the request, so that you cannot accidentily use it to something else.
Finally, some words on session duration. After being authenticated, there’s a limit in how long we trust that the user can keep his key safe. It’s good practice to end a session if you haven’t heard from that user in a while. Whether this is 15 minutes, 30 minutes or several hours depends completely on your situation. If there are ways through which you can gain more trust that the key hasn’t leaked, you can extend the session duration. E.g. if you get better at detecting fraudulent patterns and are really confident about this, you could go with much longer durations.
It’s a good idea to limit the maximum session length, even if there is user activity. For example, you may ask the user to reauthenticate every 24 hours, regardless of activity. Likely after so much time, any observed activity is the result of a script instead of a real user.
Furthermore, it is advised to renew the session ID once in a while. Especially when moving from an unauthenticated session to an authenticated session. This further decreases the risks when leaking session IDs. Often it is also adviced to renew the session Id every x minutes, regardless of whether you’re authenticating, and push a new session ID to the client. There are many considerations however that complicate such a system. E.g. when changing the session ID, requests may already be on their way to the server with the old session ID. Are you going to block those requests? How are you going to make sure that the new session ID doesn’t end up at the hacker instead of the real user? Think hard before implementing this and consider alternatives. It may also be an idea to let the user reauthenticate whenever priviledged functionality (like creating an admin user) is used.
Finally, users should be able to terminate their own session by logging out. It should even be encouraged that users use the logout button instead of just closing the browser window. This way there’s no active session left on the server without reason.
Of course most of what we discussed above can in one way or another be realized through existing frameworks. Whether you use a framework, or decide to implement all of this yourself, you must make sure that the topics above are taken care of. The advantage of using a framework is that you can be quicker up to speed, and that (if the framework is actively maintained) bugs get fixed by other people. The downside is that it adds more dependencies for you and that it’s harder to know which security considerations are made. Security is always a trade-off, so while the framework might choose the most secure option, you yourself might prefer a more user-friendly option. Make sure you have enough control to still make those choices.