Hopefully, the litany of ways attackers can mess with your sessions and session state didn’t leave you feeling hopeless about security, because there are at least as many ways you can mitigate those kinds of attacks. Here are the best practices for web applications to follow in order to protect session IDs and session state.
There are a number of best practices that can be implemented to defend against and mitigate the variety of attacks that can occur against sessions.
Enforcing Absolute Session Timeouts
To paraphrase the immortal words of Brian May, who wants sessions to live forever? Hackers, that’s who. Hackers would be delighted to have sessions never expire, because then any stolen session ID would become a permanent key to unlock your web application. The solution is obvious: Establish a maximum session lifetime, and terminate any session that reaches that limit. This creates some potential hassle for legitimate users, but having to periodically reauthenticate is not overly burdensome in practice. If you’re storing session state at the server, you can alleviate the inconvenience of forcing users to start from scratch with their tasks by restoring their session state when they reauthenticate and receive a new session ID. Basically, just destroy the expired session ID, but keep the session state around for later.
Enforcing absolute session timeouts greatly reduces the window of time in which an attacker can exploit a stolen session ID. Set your timeout to something that’s appropriate for the normal use cases your web application is designed for. A good rule of thumb is that high-security applications should limit session IDs to one or two hours, while for web applications with less stringent security requirements, a four-hour limit is good. Adjust these values based on the level of risk you’re willing to accept.
While you can (and should) specify an expiration timestamp on the session ID cookie itself, remember that you can’t trust the web client to honor that. You must also enforce session timeouts on the server. The following Set-Cookie directive shows how to specify a timeout value (in this and similar examples in this section, you will replace the cookie name and value with appropriate values for your application):
Enforcing Idle Session Timeouts
Ideally, a session ID would be destroyed as soon as the user is done interacting with the application. But, from the application server’s perspective, it can’t always know when that is. If the user explicitly logs out (see the section “Logging Out”), then it’s obvious; but if they don’t, you can’t be sure that they’re actually finished with whatever they’re doing. However, if they’re idle for a sufficiently long period of time, you can make a reasonably educated guess that they’re done. Again, evaluate your own application’s specific needs, but the best practice for high-security web applications is an idle timeout of no more than 10 minutes, and a 20-minute timeout for normal applications. Idle session timeouts must be enforced by the web application; it can either have a periodic task that sweeps the session ID table to clear out idle sessions, or it can wait for a session ID value to reappear, calculate the time since the last use, and reject if necessary.
Limiting Session Concurrency
The idea here is to limit how many active sessions a single user can have at one time. This doesn’t actually affect session IDs (because a single session ID should never be reused from one session or web client to another), but it does potentially stop an attacker from reusing stolen authentication credentials.
You might think, “why should anyone need more than one session at a time?” And indeed, that’s the most secure concurrency limit. But the reasonable limit for your application will depend on the nature of the application. For a webmail site such as Gmail, it may make perfect sense to let a single user have three sessions active at once: one is left open on their home computer, one is opened from their computer at work, and one is accessed from their smartphone. Again, consider what makes sense for your application.
Mandating Secure Cookies
One of the attributes you can set on the cookie that holds your application’s encrypted session ID is the “Secure” flag, which mandates that the web client uses encrypted communications (SSL and HTTPS) when sending the cookie back to the server along with each request. We have by now drilled home the message that you can’t trust the client to behave properly, but the Secure cookie flag is one instance where the browser enforces the behavior. Or rather, this is a case where you can expect that attackers will follow the rules because if they don’t, it’s an automatic sign that something is wrong and you should immediately invalidate the session ID. You should still check for this condition and fail in a secure manner as always, but in this particular case there’s nothing for an attacker to gain by not honoring the Secure cookie flag. The following Set-Cookie directive shows how to specify the Secure flag:
Using the HttpOnly Flag
Setting the HttpOnly flag on a cookie instructs the client that the cookie is only to be used in communications over the HTTP protocol. This effectively denies access to the cookie to any client-side code such as JavaScript. You can’t trust a malicious client not to send the cookie over some other transport, but that’s not the point. The point is that by setting this flag, honest users with honest browsers will be protected from having their cookies stolen via cross-site scripting (XSS) attacks, which rely on client-side code. The following Set-Cookie directive shows how to specify the HttpOnly flag:
Using Cryptographically Random Session IDs
Earlier, we looked at the dangers of predictable session IDs, and stated that session ID values should be unpredictable. Fortunately, it is relatively easy to do this. Just use a cryptographically secure random number generator (CSPRNG) to create them. Session ID values are nothing more than meaningless tokens anyway, useful only for their uniqueness, for which a sufficiently random number or a globally unique identifier (GUID) works just fine. If you’re using random numbers, however, remember the lesson of Jetty: random number sequences can be predictable, and not all random number generators are equal.
Destroying Invalidated Session IDs
Timeouts, expirations, unauthorized reuse—there are lots of reasons why a session ID can become invalid. Here are several best practices to follow for how to properly dispose of a session ID.
Invalidate the session ID on the server. This should be the first thing. Generally, this just means removing that session ID from whatever internal mechanism (an in-memory list, a database table) the application server is using to track the set of valid session IDs. Just delete it. This should be trivial, as most application servers and modern web development frameworks include APIs that allow you to easily manage your application’s sessions.
Immediately destroy any related client-side cookie values. If the server is still in a position to send a response to the web client, send a response that includes commands to delete the session ID cookie and any cookies containing session state information. Do this by including a Set-Cookie directive in the response with an expiration time set in the past. However, a malicious web client will ignore these.
Delete cookies when the browser closes. Again, this is more of a protection issue for honest users than anything else. An honest web browser will delete any “session cookies” when the browser closes. Session cookies are defined as cookies that have been set to expire when the session is terminated.
You might be wondering why it is recommended to delete the session ID from both the client and the server. The reason for taking it off the server is straightforward: An attacker can’t use a stolen session ID if the server disavows all knowledge of it. But why go to the bother of destroying those cookies at the client as well? Because an attacker can’t steal something that isn’t there.
Using Encrypted Cookies
Another message that should be hammered home by now: encrypt any data you store in cookies on the web client. Hopefully, your web application does not need to pass data to the client for storage at all, and can instead use server-side storage mechanisms as discussed earlier in this chapter. But there are circumstances in which it may make sense to pass state information to and from a client. In these scenarios, you must make sure that the state cannot be tampered with by an attacker, and the only way to guarantee that is to use cryptography. One way to use cryptography is to simply encrypt any data that you’re sending with a key that is not accessible to the client, so the client cannot decrypt it.
Most data encrypted with nonmalleable algorithms can’t be modified without invalidating the decryption. A nonmalleable encryption algorithm is one in which modifications to any part of an encrypted message completely invalidate the whole message. This severely curtails what an attacker can do with any cookies he or she does manage to steal. If you want to get really paranoid, why not encrypt the names of your client-side cookies too? Your web commerce application will be able to decrypt a cookie like:
into a meaningful sequence such as:
But an attacker isn’t going to have any idea what that cookie is even for, and if they try to alter the cookie, you’ll know it immediately because the decryption will yield corrupted data.
The other way to protect your session data is to use a hash-based message code (see the sidebar “Into Action,” later in this chapter), which combines the data with a secret key as a means of preventing tampering attacks. This provides for data integrity, but does not offer the same level of data confidentiality as full encryption.
Logging Out
One of the best session ID management practices you can make is simply to provide an explicit logout facility in your web application’s UI. Make it easy for users to log out with a single click. Doing so creates an explicit request to the application, which removes all doubt that a user is done with the session, allowing the server to immediately dispose of the session ID. This is the best way to minimize the window of time in which an attacker can potentially reuse a stolen session ID. Make the logout button easy to find, too.
Regenerating Session IDs on Authentication
Never reuse a session ID. Just don’t. Every time a unique visitor accesses the web application or a user authenticates, create a new session ID for that user. If every session uses a different session ID value, then session fixation attacks will fail. Of course, session fixation attacks should fail for a whole host of other reasons too, but why make it easy for an attacker? Just generate new session IDs for each unique user and on every authentication.
Authorization fundamentals
Define authorization (authZ) and how it works
Discuss examples of authorization in web applications
Cover the goals of authorization with web applications
Understand authorization as both policy definition and policy enforcement
Detailed examination of the authorization process
Define subjects and resources and show examples of each
Discuss the need to set permissions and where to set them in a web application
Review access control model types such as discretionary access control (DAC), mandatory access control (MAC), role-based access control (RBAC), and hybrid systems
Types of permission systems and where they’re applied
Understand how read, write, and execute permissions are applied under different circumstances
Discuss horizontal authorization layers such as web clients, web servers, application servers, and back-end databases
Examine vertical authorization layers including the user, application, middleware, operating system, and hardware
Implementing authorization and best practices
Discuss the different types of authorization that can be applied in the vertical and horizontal layers of a web application
Detail how to develop a custom authorization mechanism using the 3×3 model
Examine client-side attacks and how attackers use them
Understand how TOCTTOU attacks work
Review common attacks such as forceful browsing, parameter tampering, HTTP header manipulation, and CSRF
Session management fundamentals
Understand the reasons behind using sessions and session state
Catalog the different types of session state along with their pros and cons
Learn how to securely manage session state
Attacks against session management
Cover different types of session management attacks such as tampering and theft
Review the variety of interception and hijacking attacks that can occur.
Understand the threat of CSRF.
Session management best practices
Know why idle and absolute session timeouts along with the ability for a user to logout are important to securing sessions
Properly configure how your application will handle session concurrency and session invalidation
Use cookies securely and set the right flags such as HttpOnly to mitigate the impact of other attacks
Make sure that encryption is properly used when it is applied to session state management