Skip to main content

Insecure functionality - Password management

Need

Secure password management

Context

  • Usage of Python 3 for writing and executing Python code
  • Usage of Django for building web applications

Description

Non compliant code

from django.contrib.auth.models import User
from django.shortcuts import render

# This is a view for changing password
def change_password(request, username):
if request.method == 'POST':
new_password = request.POST.get('new_password')
user = User.objects.get(username=username)
user.set_password(new_password)
user.save()
return render(request, 'password_changed.html')
else:
return render(request, 'change_password.html', {'username': username})

In the above code, the change_password view is vulnerable to insecure functionality. This is because it allows any user to change the password of any other user.

The vulnerability lies in the fact that the username parameter is taken from the URL and there is no check to ensure that the user making the request is the same user whose password is being changed.

When a POST request is made to this view with a new_password parameter, the code simply fetches the user object with the given username and changes the password to the new_password.

This means that if a malicious user knows the username of another user, they can simply send a POST request to this view with their username and a new password, and they will be able to change the other user's password. This is a serious security issue as it allows for unauthorized access to other user's accounts.

Steps

  • Check if the current user is authenticated before allowing them to change the password.
  • Verify if the user requesting the password change is the same user whose password is being changed. This can be done by comparing the username in the session with the username in the URL.
  • Implement a password confirmation step. This means the user must enter their current password before they can change it to a new one.
  • Use Django's built-in views for password change. This will handle most of the security checks automatically.

Compliant code

from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect

@login_required
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
return redirect('password_changed')
else:
return render(request, 'change_password.html', {
'form': form
})
else:
form = PasswordChangeForm(request.user)
return render(request, 'change_password.html', {
'form': form
})

The updated code now uses Django's built-in PasswordChangeForm which automatically handles the validation of the old password, the creation of the new password, and the saving of the changed password.

The @login_required decorator is used to ensure that the user is authenticated before they can access the view. This decorator will redirect unauthenticated users to the login page or show an error message.

The update_session_auth_hash() function is used after saving the form to update the user's session hash. This is important because when a user's password is changed, Django logs out all sessions for that user. By calling this function, the current session will not be logged out.

The PasswordChangeForm requires the request.user instance and the POST data. If the form is valid, it saves the new password for the user, updates the session hash and then redirects to a success page. If the form is not valid (e.g., the old password is incorrect, the new passwords do not match, etc.), it will display the form again with appropriate error messages.

This approach ensures that only authenticated users can change their password, and they must know their current password to set a new one, which significantly improves the security of the password management functionality.

References