logo

Concurrent sessions control bypass - Typescript


Need

Enforcement of concurrent session control


Context

  1. Usage of TypeScript 3.0 for static typing and advanced features in JavaScript development
  2. Usage of Express for building web applications and handling HTTP requests
  3. Usage of express-session for managing session data in Express applications

Description

Insecure Code Example

import express from 'express';
import session from 'express-session';

const app = express();

app.use(session({
  secret: 'secret-key',
  resave: false,
  saveUninitialized: true,
}));

app.get('/', (req, res) => {
  if (req.session.loggedIn) {
    res.send('Welcome to the application!');
  } else {
    res.send('Please log in.');
  }
});

app.post('/login', (req, res) => {
  // Simulating a successful login
  req.session.loggedIn = true;
  res.send('Logged in successfully!');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

The vulnerability in the provided code is a concurrent sessions control bypass. In the code, the application uses the Express framework and the express-session middleware to handle sessions. The session middleware is configured with a secret key and default options. The vulnerability lies in the fact that there is no mechanism in place to invalidate previous sessions when a user logs in from a new location. This means that if a user logs in from one location and then logs in again from another location without logging out from the first session, both sessions will remain active. This vulnerability can be exploited by an attacker who can gain access to the application with the same user credentials from multiple locations simultaneously. This can lead to a loss of traceability as the application will not be able to differentiate between the legitimate user and the attacker. To mitigate this vulnerability, it is recommended to immediately invalidate the previous session when a user logs in from a new location. This can be done by implementing a mechanism to check for existing active sessions and terminating them when a new session is created.

Steps

  1. Install the 'connect-redis' package to store session data in Redis.
  2. Import the 'RedisStore' from 'connect-redis' package.
  3. Create a Redis client instance using the 'redis' package.
  4. Update the session configuration to use the RedisStore and the Redis client instance.
  5. Add a check in the '/login' route to invalidate previous sessions for the same user.
  6. Test the application to ensure the concurrent session control is working as expected.

Secure Code Example

import express from 'express';
import session from 'express-session';
import redis from 'redis';
import connectRedis from 'connect-redis';

const app = express();
const RedisStore = connectRedis(session);
const redisClient = redis.createClient();

app.use(session({
  secret: 'secret-key',
  resave: false,
  saveUninitialized: true,
  store: new RedisStore({ client: redisClient }),
}));

app.get('/', (req, res) => {
  if (req.session.loggedIn) {
    res.send('Welcome to the application!');
  } else {
    res.send('Please log in.');
  }
});

app.post('/login', (req, res) => {
  // Simulating a successful login
  req.session.loggedIn = true;

  // Invalidate previous sessions for the same user
  req.session.regenerate(() => {
    res.send('Logged in successfully!');
  });
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

The fixed code addresses the vulnerability by implementing a session management mechanism that invalidates previous sessions when a user logs in from a new location. Here's an explanation of the changes made: 1. The code imports the necessary modules: `express`, `express-session`, `redis`, and `connect-redis`. These modules are used to handle session management and store session data in Redis. 2. The code creates an instance of the `express` application and initializes a Redis store and a Redis client. 3. The `express-session` middleware is added to the application using `app.use()`. It is configured with a secret key, `resave` set to `false` to prevent unnecessary session saves, `saveUninitialized` set to `true` to save uninitialized sessions, and the Redis store created earlier. 4. The code defines a route handler for the root path ("/"). If the `loggedIn` property is set in the session, it sends a "Welcome to the application!" message. Otherwise, it sends a "Please log in." message. 5. The code defines a route handler for the "/login" path. When a successful login occurs, it sets the `loggedIn` property in the session to `true`. 6. To invalidate previous sessions for the same user, the code calls `req.session.regenerate()` after a successful login. This function regenerates the session, generating a new session ID and deleting the old session data. 7. Finally, the code starts the server and listens on port 3000. By regenerating the session after a successful login, the fixed code ensures that any previous sessions for the same user are invalidated. This prevents concurrent access to the application with the same user, addressing the vulnerability.


References

  • 301 - Concurrent sessions control bypass

  • Last updated

    2023/09/18