<- Back

Let's see why we need sessions and how to manage sessions and what are alternatives.

Why we need sessions

Sessions are required

  • to differentiate between 2 users.
  • to maintain stateful information. HTTP is stateless. So server would not know if subsequent requests come from same user.
  • Sessions are used for authentication and authorization. Once user logs in, session is created and in next requests server knows the user based on session so authorization is simple once server knows user id.
  • Personalization - Based on logged in user and session data, website can show personalized information for specific user.

Timeline

  • Early 1990s: No server side sessions. Data saved only client-side using cookies or hidden form fields.
  • Late 1990s: Server side session management using in-memory storage or database storage emerged.The session ID was stored in a cookie or URL parameter.
  • 2010s: JSON Web Tokens (JWTs) emerged as a popular alternative to traditional session management techniques. JWTs are self-contained tokens that can be used to authenticate and authorize requests without requiring a server-side session object.
  • Today: New approaches such as decentralized identity and zero-knowledge proofs are emerging

How Server Side session management works

Session Flow

  • When user logs in successfully(email/id-password/OAuth), new session is created and user information is stored in a session
  • In next requests, grab the user details from session
const session = require('express-session');
const app = express();
// Configure session middleware
app.use(session({
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}));

//Below code goes after login is successful
req.session.userId = userId; // store user ID in session

//Below code goes anywhere you want to access session data
const { userId } = req.session; // get user ID from req.session

How server side sessions can be attacked

Server-side sessions can be attacked in a variety of ways, including:

  • Session Hijacking: In session hijacking, an attacker steals the session ID of a legitimate user and uses it to impersonate that user. This can be done by intercepting the session ID as it is transmitted between the client and the server, or by guessing or brute-forcing the session ID.

  • Session Fixation: In session fixation, an attacker forces a user's session ID to a known value, which allows the attacker to hijack the user's session. This can be done by tricking the user into clicking on a malicious link or by manipulating the client-side code to set the session ID to a known value.

  • Session Replay: In session replay, an attacker intercepts a legitimate user's session data and replays it to the server in order to perform actions on behalf of the user. This can be done by intercepting and replaying the session ID and associated session data.

  • Session Timeout: In session timeout attacks, an attacker waits for a legitimate user's session to expire or be terminated due to inactivity, and then uses the same session ID to continue the session. This can be done by using a previously stolen session ID, or by guessing or brute-forcing a new session ID.

To prevent these types of attacks, web developers should follow best practices for session management, including:

  • Generating unique session IDs for each session.
  • Using secure session ID transmission protocols, such as HTTPS.
  • Implementing session timeouts and reauthentication mechanisms.
  • Using secure session data storage and encryption methods.

How JWT works

In a JWT session, the server creates a token that contains information about the user's session, such as their user ID, role, and other relevant data. This token is then sent to the client and stored in the browser's local storage or as a cookie.

When the client sends a request to the server, it includes the JWT token in the request headers or in cookie. The server then decodes the token to extract the user's session information and uses it to determine whether the user is authorized to access the requested resource.

The benefits of using JWT for session management include:

  • Stateless: The server does not need to maintain a list of active sessions since all session data is contained within the JWT token itself. This makes it easier to scale horizontally and reduces the risk of session hijacking.
  • Cross-platform: JWT can be used across multiple platforms and languages, making it easier to integrate with different services.
  • Security: JWT uses a digital signature to verify that the token has not been tampered with. This ensures that the user's session data is secure and cannot be modified by malicious actors.

Here is some example code to generate a JWT token in Node.js using the jsonwebtoken library:

const jwt = require('jsonwebtoken');

// Create a JWT token
const payload = { userId: 123 };
const secret = 'your_secret_key';
const token = jwt.sign(payload, secret, { expiresIn: '1h' });
// Set the token as a cookie
res.cookie('token', token, { httpOnly: true, maxAge: 3600000 });
// Send a response
res.send('Token sent as a cookie');

// Verify a JWT token
jwt.verify(token, secret, (err, decoded) => {
if (err) {
console.error('Invalid token');
} else {
console.log(`User ID: ${decoded.userId}`);
}
});

In this example, jwt.sign() is used to create a token with a payload object containing the user's ID. The secret parameter is used to sign the token and ensure that it has not been tampered with.

jwt.verify() is used to decode the token and extract the payload object. If the token is invalid or has been tampered with, an error is thrown. Otherwise, the decoded object contains the session data, which can be used to determine whether the user is authorized to access the requested resource.

In some apps, tokens are also stored in database for below reasons

  • Token revocation: If you store tokens in a database, you can easily revoke them if needed. For example, if a user logs out of your application, you can delete their token from the database, preventing them from using it again. This can be useful for security reasons or to enforce business logic.
  • Token management: Storing tokens in a database can make it easier to manage them. For example, you can track when tokens were issued, when they expire, and what user or session they are associated with. This can be useful for debugging, troubleshooting, or auditing.
  • Scalability: If you have a large number of users or a high volume of requests, storing tokens in a database can help improve scalability. Rather than storing all tokens in memory, which can be resource-intensive, you can store them in a database that is designed to handle large amounts of data.

You can revoke token in below ways. There are a few different ways to revoke a token, depending on your specific implementation and requirements. Here are a few common approaches:

  • Delete the token from the database: If you store tokens in a database, one way to revoke a token is to simply delete it from the database. This prevents the token from being used again, since it is no longer valid. You can also consider adding an additional field to the token table in the database to mark the token as revoked or invalidated.

  • Invalidate the token by changing its value: Another way to revoke a token is to change its value, making it no longer valid. For example, you could add a timestamp or random string to the token value that is checked by the server to validate the token. By changing this value, the token becomes invalid and can no longer be used.

  • Use a token revocation list: Instead of deleting or changing the token, you can keep a list of revoked tokens on the server. When a client sends a token in a request, the server checks the token against the revocation list to see if it has been revoked. If the token is on the list, the server rejects the request.

Disadvantages of JWT

  • You cannot as easily expire a JSON Web Token - doing so requires maintaining a server side blocklist of invalid tokens (at least until they expire) and checking every token against the list every time a token is presented.

  • Shorter session expiry times are used when using JSON Web Tokens as session tokens to allow sessions to be invalidated sooner and simplify this problem.

  • JSON Web Tokens are limited in the amount of data you can store in them. There is typically a limit of around 4096 bytes per cookie, though the exact limit varies between browsers, proxies and hosting services. If you want to support most browsers, then do not exceed 4096 bytes per cookie. If you want to save more data, you will need to persist your sessions in a database

  • Data stored in an encrypted JSON Web Token (JWE) may be compromised at some point. Even if appropriately configured, information stored in an encrypted JWT should not be assumed to be impossible to decrypt at some point - e.g. due to the discovery of a defect or advances in technology.

    How JWT based sessions can be attacked

JWT token can be stolen in below ways.

  • Man-in-the-middle (MITM) attacks: An attacker can intercept network traffic and capture the JWT, especially if the JWT is transmitted over an unsecured HTTP connection. To mitigate this risk, it's important to use HTTPS to encrypt all network traffic and prevent eavesdropping.

  • Cross-site scripting (XSS) attacks: An attacker can inject malicious code into a web page and steal the JWT from the user's browser. This can happen if the web application does not properly validate user input or sanitize user-generated content. To prevent XSS attacks, it's important to use input validation and sanitization, as well as using Content Security Policy (CSP) to prevent the injection of unauthorized scripts. CSP is implemented using an HTTP response header that is sent by the web server to the user's browser. The CSP header contains a set of rules that specify the allowed sources of content for each type of resource, such as scripts, stylesheets, images, and fonts. The rules can include a variety of types of sources, such as specific domain names, IP addresses, or protocols. For example, a CSP header that allows only content from the same domain and a specific CDN might look like this:

Content-Security-Policy: default-src 'self' https://cdn.example.com;

This rule would allow only resources from the same domain as the website, as well as from the specified CDN domain.

  • Cross-site request forgery (CSRF) attacks: An attacker can trick a user into making a request to a protected resource with a stolen JWT. This can happen if the web application does not use CSRF tokens or does not verify the origin of the request. To prevent CSRF attacks, it's important to use CSRF tokens in all forms and requests that perform sensitive actions. The CSRF token is a random value that is generated by the server and included in the form or request. When the user submits the form or request, the server verifies that the CSRF token matches the expected value for that user's session. If the token doesn't match or is missing, the server rejects the request and the action is not performed. This prevents attackers from creating a form that sends a request with the user's session cookie or JWT token, because the CSRF token is required to complete the request.

  • Storage vulnerabilities: An attacker can steal a JWT from local storage or a cookie if it's stored insecurely. To prevent storage vulnerabilities, it's important to use HttpOnly and Secure flags when setting cookies, and encrypting local storage data.

External resources

Web development and Automation testing

solutions delivered!!