Business information leak - JWT - Dart

Business information leak - JWT - Dart

Need

Protection of sensitive business information in JWT

Context

  • Usage of Dart 2.0 for building high-performance web and mobile applications
  • Usage of Shelf for managing and organizing data in Node.js applications
  • Usage of shelf_router for creating a router middleware in Shelf framework
  • Usage of jaguar_jwt for JSON Web Token (JWT) authentication and authorization

Description

Non compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf_router/shelf_router.dart';
import 'package:jaguar_jwt/jaguar_jwt.dart';

class Server {
  final JwtClaim claim;
  final String secret;
  Server(this.claim, this.secret);

  shelf.Response handleRequest(shelf.Request request) {
    final token = issueJwtHS256(claim, secret);
    return shelf.Response.ok('JWT: $token');
  }
}

void main() {
  final claim = JwtClaim(
    issuer: 'Dart',
    subject: 'JWT',
    otherClaims: {
      'username': 'example',
      'password': 'password'
    },
    maxAge: const Duration(minutes: 5),
  );

  final server = Server(claim, 'secret');

  final router = Router();
  router.get('/token', server.handleRequest);

  shelf.serve(router.handler, 'localhost', 8000);
}
        
        

The above Dart code is a basic server that uses the Shelf framework and the jaguar_jwt library to issue a JSON Web Token (JWT) upon a GET request to '/token'.

The server is initialized with a JwtClaim and a secret. The JwtClaim is a data structure that holds the payload of the JWT, which in this case includes the username and password among other claims. The secret is used to sign the JWT to ensure its integrity.

The vulnerability lies in the fact that sensitive business information, namely the username and password, are included in the JWT. JWTs are not encrypted, but merely encoded. This means that anyone who gets a hold of the JWT can easily decode it and access the sensitive information within.

In the main function, a Server object is created with a JwtClaim that includes a 'username' and 'password'. This server object is then used to handle GET requests to '/token', issuing a JWT with the sensitive business information.

This is a serious security vulnerability as it can lead to unauthorized access to the system if the JWT is intercepted by a malicious party.

Steps

  • Remove sensitive information like 'username' and 'password' from the JWT payload. JWT tokens can be easily decoded and should not contain sensitive information.
  • Use HTTPS for all communications that include JWTs to prevent man-in-the-middle attacks.
  • Implement a proper user authentication system that doesn't rely on storing passwords in JWTs. This could be a session-based system or a token-based system where the token doesn't contain sensitive information.
  • Consider using a more secure way of storing and transmitting sensitive information, such as using a secure, encrypted database.
  • Rotate the JWT secret key regularly to minimize the impact if the key is compromised.
  • Implement rate limiting to prevent brute force attacks on your JWT secret key.

Compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf_router/shelf_router.dart';
import 'package:jaguar_jwt/jaguar_jwt.dart';

class Server {
  final JwtClaim claim;
  final String secret;
  Server(this.claim, this.secret);

  shelf.Response handleRequest(shelf.Request request) {
    final token = issueJwtHS256(claim, secret);
    return shelf.Response.ok('JWT: $token');
  }
}

void main() {
  final claim = JwtClaim(
    issuer: 'Dart',
    subject: 'JWT',
    otherClaims: {
      'userId': '12345'
    },
    maxAge: const Duration(minutes: 5),
  );

  final server = Server(claim, 'secret');

  final router = Router();
  router.get('/token', server.handleRequest);

  shelf.serve(router.handler, 'localhost', 8000);
}
        
        

The updated code removes the sensitive information (username and password) from the JWT payload. Instead, it uses a non-sensitive identifier (userId) to identify the user. This identifier can be used to look up the user's information in a secure, encrypted database when needed.

The JWT token is still issued and sent to the client, but it no longer contains sensitive information that could be exploited if the token was intercepted or decoded.

Remember to use HTTPS for all communications that include JWTs to prevent man-in-the-middle attacks. Implement a proper user authentication system that doesn't rely on storing passwords in JWTs. This could be a session-based system or a token-based system where the token doesn't contain sensitive information.

Also, consider rotating the JWT secret key regularly to minimize the impact if the key is compromised. Implement rate limiting to prevent brute force attacks on your JWT secret key.

References