logo

Database

Need

Enforcement of a strong password policy, covering password complexity requirements and secure temporary password handling

Context

• Usage of Python 3 for writing and running Python code

• Usage of Django for building web applications

Description

1. Non compliant code

# --- Password strength ---
from django.contrib.auth.models import User

def create_user(request):
    username = request.POST['username']
    password = request.POST['password']

    user = User.objects.create_user(username=username, password=password)...

**Password strength:** The above code is a simple Django view function that creates a new user in the system. It takes a username and password from a POST request and uses Django's built-in `create_user` method to create a new user with these credentials. However, this code is vulnerable because it doesn't enforce any password strength requirements. A user could potentially create an account with a weak password such as "123456" or "password", which are easy to guess or crack with brute force attacks. A stronger password policy might include requirements for: - Minimum length: A longer password is generally more secure than a short one. - Complexity: The password should include a mix of uppercase and lowercase letters, numbers, and special characters. - Non-obviousness: The password should not include easily guessed information like the user's name or username. Without these checks in place, the user's account (and any data it has access to) is at risk. **Temporary passwords:** In the above code, we are creating a temporary password for a user. The function `create_temporary_password(user_id)` takes a user's id as an argument, fetches the corresponding user instance from the database, and then sets a temporary password for the user. The temporary password is created by simply reversing the username (`temp_password = user.username[::-1]`). This approach to generating a temporary password is weak and predictable, making it easy for an attacker to guess the password if they know the username. The password is then hashed using Django's `make_password` function and saved to the database (`user.password = make_password(temp_password)`). This code is vulnerable due to its weak credential policy for temporary passwords. The password strength is weak because it's derived from the username in a predictable manner. This can lead to unauthorized access if an attacker can guess the temporary password.

2. Steps

• Import the 'validate_password' function from 'django.contrib.auth.password_validation'.

• Before creating the user, call the 'validate_password' function with the password as an argument.

• The 'validate_password' function will raise a 'ValidationError' if the password does not meet Django's default password validation rules. You should catch this exception and handle it appropriately.

• If the password is valid, proceed to create the user as before.

• Implement a strong password policy for temporary passwords. This could include a minimum length, a requirement for a mix of uppercase and lowercase letters, numbers, and special characters.

• Avoid using predictable information, such as the reverse of the username, as a temporary password. Instead, generate a random string.

• Consider using a library or built-in function to generate secure random passwords.

• Ensure that temporary passwords are only used once and prompt the user to change their password after the first login.

• Store passwords securely. Use a strong, one-way hashing algorithm to store passwords.

3. Secure code example

# --- Password strength ---
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password

def create_user(request):
    username = request.POST['username']
    password = request.POST['password']...

**Password strength:** The updated code now includes a password validation step before creating a new user. This is done by importing the `validate_password` function from `django.contrib.auth.password_validation`. The `validate_password` function is called with the password as an argument. If the password does not meet Django's default password validation rules, the function will raise a `ValidationError`. This exception is caught and handled by returning a dictionary with a status of 'error' and a message containing the error details. If the password is valid, the user is created and saved as before, and a success message is returned. This way, the system now enforces a strong credential policy, mitigating the risk of weak passwords. **Temporary passwords:** The updated code now generates a secure temporary password for the user. Here's how it works: 1. We import the necessary libraries. `string` and `random` are Python's built-in libraries. `string` contains various string constant which contains the ASCII characters of all cases. `random` is used to generate random numbers. 2. In the `create_temporary_password` function, we first retrieve the user object from the database using the provided `user_id`. 3. We then generate a random temporary password. The `random.choices()` function returns a list with a randomly selection from the given iterable (in our case, a combination of uppercase and lowercase ASCII letters, digits, and punctuation). We specify that we want a list of 10 elements. The `join()` function then combines this list into a single string. 4. We use Django's `make_password` function to hash the password before storing it in the database. This is a security best practice, as it means that even if someone gains access to the database, they won't be able to see the users' actual passwords. 5. Finally, we save the user object with the updated password. This code ensures that the temporary password is both secure (as it's randomly generated and hashed) and unique to each user. The user should be prompted to change this temporary password after their first login.