Weak credential policy - Password Change Limit - Python

Weak credential policy - Password Change Limit - Python

Need

Implementation of a strong password change policy

Context

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

Description

Non compliant code

        from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordChangeForm

def change_password(request):
    if request.method == 'POST':
        form = PasswordChangeForm(user=request.user, data=request.POST)
        if form.is_valid():
            form.save()
            update_session_auth_hash(request, form.user)
            return redirect('change_password_done')
    else:
        form = PasswordChangeForm(user=request.user)
    return render(request, 'change_password.html', {'form': form})
        
        

This Python code is using the Django framework to handle password change requests for a user. The change_password function is invoked when a POST request is made to the server. This function takes in the user's request, creates a PasswordChangeForm with the user's data, and then checks if the form is valid. If the form is valid, the password is saved and the session auth hash is updated.

The vulnerability in this code is that it does not limit the number of password change requests that can be made in a day. This means that an attacker could potentially make unlimited password change requests, which could lead to a denial of service (DoS) attack or could be used as part of a brute force attack to guess the user's password.

Steps

  • Create a new model or table in the database to store the password change history for each user. This table should include fields for the user ID, the timestamp of the password change, and possibly the new password hash.
  • In the 'change_password' function, before allowing the password change, check the password change history for the current user. Count the number of password changes that have occurred in the last 24 hours.
  • If the number of password changes in the last 24 hours exceeds a certain limit (for example, 3), do not allow the password change and return an error message to the user.
  • If the number of password changes in the last 24 hours is below the limit, allow the password change and add a new entry to the password change history for the user.
  • Ensure that the password change limit and the time period (24 hours) are configurable, so that they can be easily adjusted if necessary.

Compliant code

        from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordChangeForm
from django.utils import timezone
from .models import PasswordChangeHistory

def change_password(request):
    if request.method == 'POST':
        form = PasswordChangeForm(user=request.user, data=request.POST)
        if form.is_valid():
            # Check the password change history for the current user
            now = timezone.now()
            past_24_hours = now - timezone.timedelta(days=1)
            changes = PasswordChangeHistory.objects.filter(user=request.user, timestamp__gte=past_24_hours).count()
            
            # Limit the number of password changes in the last 24 hours
            if changes >= 3:
                return render(request, 'change_password.html', {'form': form, 'error': 'You have reached the limit of password changes in the last 24 hours.'})
            
            form.save()
            update_session_auth_hash(request, form.user)
            
            # Add a new entry to the password change history
            PasswordChangeHistory.objects.create(user=request.user, timestamp=now)
            
            return redirect('change_password_done')
    else:
        form = PasswordChangeForm(user=request.user)
    return render(request, 'change_password.html', {'form': form})
        
        

The updated code now includes a check for the number of password changes made by the user in the last 24 hours before allowing another password change. This is done by querying a new PasswordChangeHistory model, which stores the history of password changes for each user.

If the number of password changes in the last 24 hours is 3 or more, the password change is not allowed and an error message is returned to the user. If the number of password changes is less than 3, the password change is allowed and a new entry is added to the PasswordChangeHistory model.

The PasswordChangeHistory model should be defined in your models.py file and should include fields for the user (a foreign key to the User model), and the timestamp of the password change.

This solution ensures that users cannot change their password more than a certain number of times in a 24 hour period, helping to prevent attacks that rely on rapidly changing passwords.

References