Skip to main content

Password change without identity check

Need

Stronger password change authentication

Context

  • Usage of Python 3 for developing applications and scripts
  • Usage of Django for building web applications

Description

Non compliant code

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.shortcuts import render, redirect

@login_required
def change_password(request):
if request.method == 'POST':
new_password = request.POST.get('new_password')
user = User.objects.get(username=request.user.username)
user.set_password(new_password)
user.save()
return redirect('home')
else:
return render(request, 'change_password.html')

The above code represents a Django view function change_password that allows logged in users to change their password. The function checks if the request method is POST and then retrieves the new password from the request data. It then retrieves the user object from the database using the username from the current user session. The password for the user is then set to the new password and the user object is saved, effectively changing the password.

The vulnerability here is that this function does not verify the identity of the user before changing the password. It only checks if the user is logged in, but does not ask for the current password or any other form of identity verification. This means that if a user's session is hijacked or if they leave their device unattended and logged in, another person could change the user's password without their knowledge.

Steps

  • Add a form field to request the current password from the user.
  • In the POST method, retrieve the current password from the form data.
  • Use Django's built-in check_password function to verify if the current password entered by the user matches the one stored in the database.
  • If the passwords match, allow the user to set a new password. If they don't match, return an error message to the user.
  • Consider adding additional identity verification mechanisms such as two-factor authentication or security questions.

Compliant code

from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
from django.shortcuts import render, redirect

@login_required
def change_password(request):
if request.method == 'POST':
current_password = request.POST.get('current_password')
new_password = request.POST.get('new_password')
user = User.objects.get(username=request.user.username)

if check_password(current_password, user.password):
user.set_password(new_password)
user.save()
return redirect('home')
else:
return render(request, 'change_password.html', {'error': 'Incorrect current password'})
else:
return render(request, 'change_password.html')

The updated code now includes a check for the current password before allowing the user to change their password.

In the POST method, we now retrieve the current password from the form data using request.POST.get('current_password').

We then use Django's built-in check_password function to verify if the current password entered by the user matches the one stored in the database. This function takes two arguments: the plain-text password entered by the user and the hashed password stored in the database. It returns True if the passwords match and False otherwise.

If the passwords match, we allow the user to set a new password and save the changes. If they don't match, we return an error message to the user and render the password change form again.

This update ensures that the user's identity is verified before allowing a password change, thus fixing the vulnerability.

For additional security, consider implementing two-factor authentication or security questions.

References