Common Security Mistakes in Django and How to Fix Them

Search for a command to run...

No comments yet. Be the first to comment.
There's a question that most people feel but almost nobody says out loud. Not because it's complicated. Because saying it threatens everything built on top of not saying it. The question is simple. Wh

Public discourse around personal finance in India increasingly relies on lifestyle narratives, absolute numbers, and loosely imported benchmarks. Concepts such as middle class, financial security, high income, or wealthy are often used without refere...

By Ahmad W Khan (interactive at https://ahmadwkhan.com/india-work-landscape) Summary (TL;DR): India’s labour market is vast, diversified, and uneven. Government roles remain tenure‑rich but hard to enter; healthcare and licensed professional practice...

Audience: Intermediate PHP devs (comfortable with OOP, Composer, basic MVC) who are new/rusty with SymfonyOS Assumptions: macOS/Linux primary; Windows notes included (PowerShell + WSL2)Target PHP & Symfony: PHP 8.2+ and Symfony 7.3.x (current stable ...

Money, unlike geography, is invisible.We know where a country starts and ends on a map. But wealth and income? They’re like air currents, everywhere, yet hard to see. Percentiles help make them visible: where do you sit compared to your neighbors, yo...

Django is a powerful and secure web framework, but like any tool, its security depends on how developers use it. Many security vulnerabilities arise from misconfigurations, poor coding practices, or a lack of awareness about potential threats. Let’s go through some of the most common security mistakes in Django applications and how to fix them.
DEBUG=True in ProductionWhen DEBUG=True, Django provides detailed error messages that include sensitive information like environment variables, database connection details, and application settings. This is great for debugging but dangerous in production.
Always set DEBUG=False in production and ensure sensitive information isn’t leaked.
DEBUG = False
ALLOWED_HOSTS = ["yourdomain.com"]
Additionally, use environment variables to manage settings securely:
import os
DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"
Many developers accidentally commit settings.py to version control with their SECRET_KEY exposed. Attackers can use this key to generate valid session cookies or even sign malicious requests.
Move sensitive values to environment variables and keep them out of version control.
import os
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "your-default-secret-key")
Use a .env file and load it with django-environ:
# .env file
DJANGO_SECRET_KEY=your-very-secret-key
And load it in settings.py:
import environ
env = environ.Env()
environ.Env.read_env()
SECRET_KEY = env("DJANGO_SECRET_KEY")
Also, add *.env to your .gitignore to prevent committing secrets.
By default, Django does not enforce SSL/TLS encryption in database connections, leaving data vulnerable to interception.
Use strong database configurations, especially for production:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST'),
'PORT': env('DB_PORT'),
'OPTIONS': {
'sslmode': 'require', # Enforce SSL connection
},
}
}
For PostgreSQL, ensure that you require SSL in your database settings.
Using weak password hashing algorithms or storing plain-text passwords in the database.
Django uses PBKDF2 by default, but you can switch to Argon2, which is more resistant to brute-force attacks.
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
Never store passwords in plaintext and always use Django's set_password() to hash passwords before saving.
from django.contrib.auth.models import User
user = User.objects.create(username='secureuser')
user.set_password('Secure@123') # Hashes the password
user.save()
Forgetting to use CSRF tokens on forms, allowing attackers to perform Cross-Site Request Forgery (CSRF) attacks.
Ensure that every form submission includes a CSRF token:
<form method="post">
{% csrf_token %}
<input type="text" name="username">
<button type="submit">Submit</button>
</form>
For API-based requests, ensure the CSRF middleware is properly handled by using CSRF exempt views only when necessary:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
pass # Only use if absolutely necessary
Use Django’s built-in CSRF protection headers for AJAX requests:
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
fetch('/api/endpoint/', {
method: 'POST',
headers: { 'X-CSRFToken': csrftoken },
body: JSON.stringify({ data: "value" })
});
Using raw SQL queries with unescaped input allows SQL injection, where attackers can manipulate queries to extract or modify data.
# Vulnerable code
username = request.GET.get('username')
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query) # SQL Injection risk
Always use Django's ORM instead of raw queries:
from django.db.models import Q
User.objects.filter(Q(username=username))
If you must use raw SQL, use parameterized queries:
cursor.execute("SELECT * FROM users WHERE username = %s", [username])
Serving your site over HTTP exposes sensitive user data, including login credentials and session cookies.
Force HTTPS by setting SECURE_SSL_REDIRECT:
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Redirect all HTTP traffic to HTTPS in Nginx:
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
Use Let's Encrypt or other SSL providers to enable HTTPS.
Leaving the Django Admin panel exposed at /admin/ makes it an easy target for brute-force attacks.
Change the default admin URL:
urlpatterns = [
path("secureadmin/", admin.site.urls),
]
Additionally, restrict access using IP whitelisting:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_ip_restrict.middleware.IPRestrictMiddleware",
]
IP_RESTRICTOR_ALLOW_LIST = ['YOUR_IP_ADDRESS']
Use Django Admin Honeypot to mislead attackers:
pip install django-admin-honeypot
And add to your urls.py:
import honeypot
urlpatterns = [
path("admin/", include("honeypot.urls", namespace="honeypot")),
]
Django’s default session storage allows session hijacking if not properly secured.
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_COOKIE_AGE = 3600 # 1 hour
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
Django provides excellent security features out-of-the-box, but it's up to developers to configure and use them properly. By following these best practices, you can significantly reduce security risks and build a safer Django application.
Security isn’t a one-time effort—it’s a continuous process. Regularly audit your code, stay updated with Django security releases, and use tools like django-secure, bandit, and django-security-checker to automate security checks.
For more such tips, visit me at AhmadWKhan.com