How To Lock Out User after 3 failed login attempts in Django

Introduction: In Django web applications, ensuring robust security measures is paramount to protect against unauthorized access and potential breaches. One effective strategy is implementing a mechanism to lock out users after a certain number of failed login attempts. This proactive approach helps mitigate brute force attacks and enhances the overall security posture of the application.

Benefits:

  1. Enhanced Security: Implementing a lockout mechanism after three failed login attempts significantly strengthens the security of Django applications. It acts as a deterrent to malicious actors attempting unauthorized access through brute force attacks.
  2. Proactive Defense: By promptly locking out users after a predefined number of failed login attempts, the system proactively defends against potential security threats before they can escalate into successful breaches.
  3. Reduced Risk of Unauthorized Access: Locking out users after a limited number of failed login attempts reduces the risk of unauthorized access to sensitive information or functionalities within the application.
  4. Customizable Configuration: The lockout mechanism in Django allows for customization of parameters such as the maximum number of failed login attempts and the duration of the lockout period. This flexibility enables administrators to tailor security measures to suit the specific needs of their application.
  5. User-Friendly Feedback: While enforcing security measures, it’s essential to maintain a positive user experience. Django’s lockout mechanism can provide clear and informative feedback to users, informing them of the reason for the lockout and guiding them on the next steps to regain access.

Lets Get Started

After setting up your Django project and configuring your app, the next step is to create a middleware.py file within your app directory. This file will host middleware classes responsible for processing HTTP requests and responses. Middleware plays a vital role in implementing cross-cutting concerns like authentication and security. By creating and configuring middleware, you can enhance the functionality and security of your Django application efficiently.

# middleware.py
from django.conf import settings
from django.contrib.auth.models import User
from django.core.cache import cache
from django.http import HttpResponseForbidden

class IPLockMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        ip = self.get_client_ip(request)
        username = request.POST.get('username')  # Assuming username is submitted via POST
        if ip:
            key_ip = f'failed_login_attempts_{ip}'
            attempts_ip = cache.get(key_ip, 0)
            if attempts_ip >= settings.MAX_FAILED_LOGIN_ATTEMPTS:
                return HttpResponseForbidden("Your IP has been temporarily locked due to multiple failed login attempts.")
            # Check user attempts
            if username:
                key_user = f'failed_login_attempts_{username}'
                attempts_user = cache.get(key_user, 0)
                if attempts_user >= settings.MAX_FAILED_LOGIN_ATTEMPTS:
                    return HttpResponseForbidden("Your account has been temporarily locked due to multiple failed login attempts.")
        response = self.get_response(request)
        return response

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

After that, go to your settings.py file and paste this code inside the middleware section.

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app1.middleware.IPLockMiddleware',
]

MAX_FAILED_LOGIN_ATTEMPTS = 3
FAILED_LOGIN_LOCK_DURATION = 3600  # 1 hour in seconds

Now go to views.py and paste this code

from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login, logout
from django.contrib import messages
from django.core.cache import cache
from django.conf import settings

def home(request):
    return render(request, 'home.html')

def register_view(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
    else:
        form = UserCreationForm()
    return render(request, 'register.html', {'form': form})

def login_view(request):
    if request.method == 'POST':
        form = AuthenticationForm(data=request.POST)
        if form.is_valid():
            user = form.get_user()
            login(request, user)
            # Reset failed login attempts for this IP upon successful login
            ip = get_client_ip(request)
            if ip:
                key = f'failed_login_attempts_{ip}'
                cache.delete(key)
            return redirect('home')  # Redirect to some page after login
        else:
            # Increment failed login attempts for this IP
            ip = get_client_ip(request)
            if ip:
                key = f'failed_login_attempts_{ip}'
                attempts = cache.get(key, 0)
                attempts += 1
                cache.set(key, attempts, timeout=settings.FAILED_LOGIN_LOCK_DURATION)
                if attempts >= settings.MAX_FAILED_LOGIN_ATTEMPTS:
                    messages.error(request, "Your IP has been temporarily locked due to multiple failed login attempts.")
    else:
        form = AuthenticationForm()
    return render(request, 'login.html', {'form': form})

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def logout_view(request):
    logout(request)
    return redirect('login')

Now go to project urls.py and paste this code

from django.contrib import admin
from django.urls import path
from app1 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home, name='home'),
    path('register/', views.register_view, name='register'),
    path('login', views.login_view, name='login'),
    path('logout/', views.logout_view, name='logout'),
]

Not go to you are app directory and create templates folder inside the template folder create home.html file


<!-- home.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
<style>
/* Basic Styling */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: #fff;
padding: 10px 0;
text-align: center;
}
nav {
background-color: #444;
text-align: center;
padding: 10px 0;
}
nav a {
color: #fff;
text-decoration: none;
padding: 10px 20px;
}
nav a:hover {
background-color: #555;
}
.container {
max-width: 800px;
margin: 20px auto;
padding: 0 20px;
}
/* Responsive Layout */
@media only screen and (max-width: 600px) {
nav a {
padding: 10px;
}
}
/* Footer Styling */
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 10px 0;
position: fixed;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<header>
<h1>Welcome to Your Website</h1>
</header>
<nav>
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact Us</a>
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">Logout</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
<a href="{% url 'register' %}">Register</a>
{% endif %}
</nav>
<div class="container">
<h2>About Us</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla facilisi. Sed non bibendum velit. Phasellus consequat, dui vel tincidunt consectetur, lectus ligula ultrices sapien, ut finibus lectus erat ac nulla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc sed erat id risus tempus suscipit. Proin vitae quam id turpis interdum ullamcorper eu sed metus. In hac habitasse platea dictumst. In eget semper metus. Duis feugiat, libero at laoreet mollis, orci arcu scelerisque libero, a sagittis ligula dolor nec metus. Fusce dapibus lorem ac orci varius, eget euismod eros tincidunt. Mauris sed malesuada mi.</p>
<h2>Contact Us</h2>
<p>Email: info@example.com</p>
<p>Phone: +1234567890</p>
</div>
<footer>
&copy; 2024 Your Website. All rights reserved.
</footer>
<script>
// Example JavaScript functionality (optional)
document.addEventListener("DOMContentLoaded", function() {
// Your JavaScript code here
});
</script>
</body>
</html>

Login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }

        .login-container {
            background-color: #fff;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            max-width: 400px;
            width: 100%;
        }

        h2 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        form {
            display: flex;
            flex-direction: column;
        }

        input[type="text"],
        input[type="password"] {
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
        }

        button[type="submit"] {
            background-color: #007bff;
            color: #fff;
            border: none;
            padding: 10px;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
        }

        button[type="submit"]:hover {
            background-color: #0056b3;
        }

        .error-message {
            color: red;
            margin-top: 10px;
        }

        .register-link {
            text-align: center;
            margin-top: 20px;
        }

        .register-link a {
            color: #007bff;
            text-decoration: none;
        }

        .register-link a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="login-container">
        <h2>Login</h2>
        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Login</button>
        </form>
        {% if form.errors %}
            <p class="error-message">Invalid username or password.</p>
        {% endif %}
        <div class="register-link">
            <p>Don't have an account? <a href="{% url 'register' %}">Register here</a></p>
        </div>
    </div>
</body>
</html>

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Register</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }

        .register-container {
            background-color: #fff;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            max-width: 400px;
            width: 100%;
        }

        h2 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        form {
            display: flex;
            flex-direction: column;
        }

        input[type="text"],
        input[type="password"] {
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
        }

        button[type="submit"] {
            background-color: #007bff;
            color: #fff;
            border: none;
            padding: 10px;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
        }

        button[type="submit"]:hover {
            background-color: #0056b3;
        }

        .error-message {
            color: red;
            margin-top: 10px;
        }

        .login-link {
            text-align: center;
            margin-top: 20px;
        }

        .login-link a {
            color: #007bff;
            text-decoration: none;
        }

        .login-link a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="register-container">
        <h2>Register</h2>
        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Register</button>
        </form>
        <div class="login-link">
            <p>Already have an account? <a href="{% url 'login' %}">Login here</a></p>
        </div>
    </div>
</body>
</html>

In summary, integrating a lockout mechanism following three unsuccessful login attempts within Django provides numerous advantages, such as heightened security, preemptive measures against threats, and adaptable configuration settings. By embracing this strategy, Django developers can notably fortify their application’s security stance and shield against potential security breaches. Additionally, I recommend checking out YouTube videos that delve into Django security practices for further insights and tips on securing your Django applications effectively.

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Basket