Django: Password Management
Password management is a critical aspect of web application security, ensuring user credentials are stored securely and handled effectively. Django’s built-in authentication system, part of the Model-View-Template (MVT) architecture and the django.contrib.auth
package, provides robust tools for secure password hashing, validation, and user-initiated password changes or resets. This tutorial explores Django password management, covering setup, customization, and practical applications for secure credential handling.
01. Why Focus on Password Management?
Secure password management protects user accounts from unauthorized access and data breaches. Django’s authentication system offers secure password hashing, built-in views for password changes and resets, and password validation to enforce strong policies. These features are essential for applications like blogs, e-commerce platforms, or internal dashboards, ensuring compliance with security best practices while providing a seamless user experience.
Example: Basic Password Hashing
# myapp/views.py
from django.contrib.auth.models import User
def create_user(request):
user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='SecurePass123!'
)
print(user.password) # Hashed password
return HttpResponse("User created")
Output:
pbkdf2_sha256$720000$... (hashed password)
Explanation:
create_user
- Automatically hashes the password using PBKDF2 with SHA256.- Hashed passwords are stored securely in the database.
02. Key Password Management Concepts
Django’s authentication system provides a comprehensive framework for managing passwords. The table below summarizes key components and their roles:
Component | Description | Use Case |
---|---|---|
Password Hashing | Securely stores passwords using cryptographic algorithms | Protect user credentials |
Password Validators | Enforces password strength policies | Ensure strong passwords |
Password Change | Allows users to update their password | User account maintenance |
Password Reset | Enables password recovery via email | Handle forgotten passwords |
2.1 Configuring Password Validators
Example: Enforcing Strong Passwords
# myproject/settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 10},
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
Output:
Passwords must be at least 10 characters, not common, not numeric, and not similar to user attributes.
Explanation:
AUTH_PASSWORD_VALIDATORS
- Enforces password strength during creation or change.- Validators prevent weak passwords, enhancing security.
2.2 Implementing Password Change
Example: Password Change View
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
]
# myapp/templates/registration/password_change_form.html
{% extends "base.html" %}
{% block content %}
<h2>Change Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Change Password</button>
</form>
{% endblock %}
Output:
Password change form at http://127.0.0.1:8000/accounts/password_change/.
Explanation:
django.contrib.auth.urls
- Includespassword_change/
URL.- Custom template enhances the user interface.
2.3 Setting Up Password Reset
Example: Password Reset with Email
# myproject/settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-app-password'
DEFAULT_FROM_EMAIL = 'your-email@gmail.com'
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
]
# myapp/templates/registration/password_reset_form.html
{% extends "base.html" %}
{% block content %}
<h2>Reset Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send Reset Link</button>
</form>
{% endblock %}
# myapp/templates/registration/password_reset_email.html
{% autoescape off %}
To reset your password, click the link below:
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
If you did not request a password reset, ignore this email.
{% endautoescape %}
Output:
Password reset form at http://127.0.0.1:8000/accounts/password_reset/ sends email with reset link.
Explanation:
EMAIL_BACKEND
- Configures email sending for password reset.- Built-in views handle reset link generation and verification.
2.4 Custom Password Change Form
Example: Custom Password Change
# myapp/forms.py
from django import forms
from django.contrib.auth.forms import PasswordChangeForm
class CustomPasswordChangeForm(PasswordChangeForm):
old_password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
new_password1 = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
new_password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
def clean_new_password2(self):
new_password1 = self.cleaned_data.get('new_password1')
new_password2 = self.cleaned_data.get('new_password2')
if new_password1 and new_password2 and new_password1 != new_password2:
raise forms.ValidationError("New passwords do not match.")
return new_password2
# myapp/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from .forms import CustomPasswordChangeForm
@login_required
def change_password(request):
if request.method == 'POST':
form = CustomPasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
return redirect('dashboard')
else:
form = CustomPasswordChangeForm(user=request.user)
return render(request, 'myapp/change_password.html', {'form': form})
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('change-password/', views.change_password, name='change_password'),
]
# myapp/templates/myapp/change_password.html
{% extends "base.html" %}
{% block content %}
<h2>Change Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Change Password</button>
</form>
{% endblock %}
Output:
Custom password change form at http://127.0.0.1:8000/change-password/.
Explanation:
PasswordChangeForm
- Validates old and new passwords.- Custom form adds styling and extra validation.
2.5 Incorrect Password Management
Example: Storing Plain-Text Passwords
# myapp/views.py (Incorrect)
from django.contrib.auth.models import User
from django.shortcuts import redirect
def register(request):
User.objects.create(
username=request.POST['username'],
password=request.POST['password'] # Incorrect: Plain text
)
return redirect('login')
Output:
Passwords stored unhashed, vulnerable to breaches.
Explanation:
- Plain-text passwords are insecure and easily compromised.
- Solution: Use
set_password
orcreate_user
for hashing.
03. Effective Usage
3.1 Recommended Practices
- Use Django’s built-in views and password validators for secure, minimal-code solutions.
Example: Comprehensive Password Management
# myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
]
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 10}},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@gmail.com'
EMAIL_HOST_PASSWORD = 'your-app-password'
DEFAULT_FROM_EMAIL = 'your-email@gmail.com'
LOGIN_REDIRECT_URL = 'dashboard'
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', include('myapp.urls')),
]
# myapp/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.contrib.auth.forms import PasswordChangeForm
@login_required
def dashboard(request):
return render(request, 'myapp/dashboard.html', {'user': request.user})
@login_required
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
return redirect('dashboard')
else:
form = PasswordChangeForm(user=request.user)
return render(request, 'myapp/change_password.html', {'form': form})
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('dashboard/', views.dashboard, name='dashboard'),
path('change-password/', views.change_password, name='change_password'),
]
# myapp/templates/myapp/change_password.html
{% extends "base.html" %}
{% block content %}
<h2>Change Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Change Password</button>
</form>
{% endblock %}
# myapp/templates/registration/password_reset_form.html
{% extends "base.html" %}
{% block content %}
<h2>Reset Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send Reset Link</button>
</form>
{% endblock %}
Output:
Secure password change at http://127.0.0.1:8000/change-password/ and reset at http://127.0.0.1:8000/accounts/password_reset/.
- Validators enforce strong passwords.
- Built-in views handle password change and reset securely.
- Email configuration supports password recovery.
3.2 Practices to Avoid
- Avoid disabling password validators or using insecure email configurations.
Example: Disabling Validators
# myproject/settings.py (Incorrect)
AUTH_PASSWORD_VALIDATORS = []
Output:
Weak passwords allowed, increasing security risks.
- Empty validators allow insecure passwords.
- Solution: Include robust
AUTH_PASSWORD_VALIDATORS
.
04. Common Use Cases
4.1 Secure User Registration
Ensure passwords are securely hashed during registration.
Example: Registration with Validation
# myapp/forms.py
from django import forms
from django.contrib.auth.models import User
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'email', 'password']
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password_confirm = cleaned_data.get('password_confirm')
if password and password_confirm and password != password_confirm:
raise forms.ValidationError("Passwords do not match.")
return cleaned_data
# myapp/views.py
from django.shortcuts import render, redirect
from .forms import RegisterForm
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.save()
return redirect('login')
else:
form = RegisterForm()
return render(request, 'myapp/register.html', {'form': form})
Output:
Secure registration at http://127.0.0.1:8000/register/ with hashed passwords.
Explanation:
set_password
- Hashes passwords securely.- Form validation ensures strong passwords.
4.2 Password Recovery for Users
Provide a secure password reset flow for forgotten passwords.
Example: Custom Password Reset
# myapp/templates/registration/password_reset_confirm.html
{% extends "base.html" %}
{% block content %}
<h2>Set New Password</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Set Password</button>
</form>
{% endblock %}
Output:
Password reset confirmation page after clicking email link.
Explanation:
- Custom template improves reset form usability.
- Built-in views handle secure token-based reset.
Conclusion
Django’s password management, integrated with the Model-View-Template architecture, provides a secure and efficient framework for handling user credentials. Key takeaways:
- Use
set_password
and validators for secure password storage. - Leverage built-in views for password change and reset.
- Configure email for secure password recovery.
- Avoid plain-text passwords or weak validation.
With Django’s password management tools, you can build secure, user-friendly authentication systems for web applications!
Comments
Post a Comment