Skip to main content

Business information leak - JWT

Need

Protection of sensitive business information in JWT

Context

  • Usage of Python 3.0+ for developing Python applications
  • Usage of JWT for secure authentication and authorization
  • Usage of Django for building web applications in Python
  • Usage of Django Rest Framework for building RESTful APIs in Django

Description

Non compliant code

import jwt
from django.http import JsonResponse
from rest_framework.decorators import api_view

SECRET_KEY = 'SECRET'

@api_view(['POST'])
def login(request):
username = request.data.get('username')
password = request.data.get('password')

# Assume we have a function authenticate that checks the username and password
user = authenticate(username, password)
if user is not None:
payload = {
'username': username,
'password': password
}
jwt_token = jwt.encode(payload, SECRET_KEY)
return JsonResponse({'token': jwt_token})
else:
return JsonResponse({'error': 'Invalid username or password'}, status=400)

In the above code, we have a Django view login that takes a username and password from a POST request, authenticates the user, and if the authentication is successful, it generates a JSON Web Token (JWT) that contains the username and password in its payload.

The vulnerability here is that the JWT contains sensitive information (the password) in its payload. JWTs are not encrypted; they are just base64 encoded. This means that if someone gets their hands on the token, they can easily decode it and retrieve the sensitive information inside it.

In addition, the JWT is signed with a secret key to ensure its integrity. However, if the secret key is weak or leaked, an attacker could forge tokens. This could lead to unauthorized access to the application, potentially leading to further attacks such as data leakage or manipulation.

This is a serious security issue because it exposes sensitive user information and potentially the whole system to attackers.

Steps

  • Remove sensitive information from the JWT payload. The payload should not contain sensitive information like passwords.
  • Instead of the password, store the user's ID or another unique identifier in the JWT payload. This identifier can be used to retrieve the user's information when needed.
  • Ensure that the JWT token is sent over HTTPS to prevent interception during transmission.
  • Consider using a more secure way to authenticate users, such as OAuth 2.0 or OpenID Connect, instead of JWT.

Compliant code

import jwt
from django.http import JsonResponse
from rest_framework.decorators import api_view

SECRET_KEY = 'SECRET'

@api_view(['POST'])
def login(request):
username = request.data.get('username')
password = request.data.get('password')

# Assume we have a function authenticate that checks the username and password
user = authenticate(username, password)
if user is not None:
payload = {
'user_id': user.id, # Use user's ID instead of username and password
}
jwt_token = jwt.encode(payload, SECRET_KEY)
return JsonResponse({'token': jwt_token})
else:
return JsonResponse({'error': 'Invalid username or password'}, status=400)

The updated code removes the sensitive information (username and password) from the JWT payload. Instead, it includes the user's ID, which is a unique identifier that can be used to retrieve the user's information when needed. This change prevents the exposure of sensitive user information in the JWT token.

In addition, it's important to ensure that the JWT token is sent over HTTPS to prevent interception during transmission. This is not shown in the code, but it's a crucial part of securing the JWT token.

Finally, while JWT can be used for authentication, it's often recommended to use a more secure method such as OAuth 2.0 or OpenID Connect. These methods provide additional security features that can help protect against various types of attacks.

References