Best Practices for Securing Django in Production

Ahmad W KhanAhmad W Khan
4 min read

Deploying Django in production requires careful security configurations, especially when using cloud platforms like AWS, DigitalOcean, GCP, or Azure. While Django provides security features out of the box, a misconfigured production environment can expose your application to attacks.

This guide will expand on common security mistakes and how to configure Django securely in production with best practices for AWS, DigitalOcean, GCP, and other cloud platforms.

1. Setting Up Django’s Production Security Configurations

1.1 Disable Debug Mode

Leaving DEBUG=True in production is a major security risk. It exposes environment variables, database credentials, and internal stack traces.

Fix:

import os

DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"
ALLOWED_HOSTS = ["yourdomain.com"]

Use environment variables to manage settings securely instead of hardcoding them.

1.2 Restrict Allowed Hosts

Only allow requests from your domain:

ALLOWED_HOSTS = ["yourdomain.com", "api.yourdomain.com"]

For cloud-based environments (AWS/GCP), set ALLOWED_HOSTS dynamically:

ALLOWED_HOSTS = os.getenv("DJANGO_ALLOWED_HOSTS", "").split(",")

2. Securing Database in Production

2.1 Secure Database Credentials Using Environment Variables

Never hardcode database credentials in settings.py. Instead, store them in AWS SSM Parameter Store, GCP Secret Manager, DigitalOcean App Secrets, or .env files.

Example for AWS RDS / PostgreSQL:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv("DB_NAME"),
        'USER': os.getenv("DB_USER"),
        'PASSWORD': os.getenv("DB_PASSWORD"),
        'HOST': os.getenv("DB_HOST"),  # RDS instance or Cloud SQL
        'PORT': os.getenv("DB_PORT", "5432"),
        'OPTIONS': {
            'sslmode': 'require',  # Enforce SSL
        },
    }
}

2.2 Use IAM Roles for RDS (AWS)

Instead of storing passwords, use IAM authentication with AWS RDS:

psql "host=your-rds-instance.amazonaws.com dbname=yourdb sslmode=verify-full sslrootcert=rds-ca.pem"

2.3 Enable SSL for Database Connections

For PostgreSQL on AWS RDS, DigitalOcean DBaaS, or GCP Cloud SQL, enforce SSL to encrypt data in transit.

'OPTIONS': {
    'sslmode': 'require',
}

Verify with:

psql "sslmode=require"

3. Securing Static and Media Files

3.1 Use a CDN for Static & Media Files

Serving static and media files via S3, DigitalOcean Spaces, or Google Cloud Storage (GCS) reduces server load and increases security.

Example for AWS S3:

INSTALLED_APPS += ["storages"]

AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")
AWS_S3_CUSTOM_DOMAIN = f"{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com"
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"

For DigitalOcean Spaces:

DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
AWS_S3_ENDPOINT_URL = "https://nyc3.digitaloceanspaces.com"

For Google Cloud Storage (GCP):

DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_BUCKET_NAME = os.getenv("GS_BUCKET_NAME")

3.2 Restrict Public Access to Sensitive Files

  • Set private permissions for user-uploaded files.

  • Use pre-signed URLs for downloads.

AWS_S3_OBJECT_PARAMETERS = {
    "CacheControl": "max-age=86400",
    "ACL": "private"
}

4. Secure Authentication & Sessions

4.1 Use Secure Password Hashing

Enable Argon2 for better security:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
]

4.2 Enforce HTTPS for Sessions

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

4.3 Enable Multi-Factor Authentication (MFA)

Use django-otp for MFA in Django Admin:

pip install django-otp

Add to INSTALLED_APPS:

INSTALLED_APPS += ["django_otp", "django_otp.plugins.otp_totp"]

5. Protect Against SQL Injection & XSS

5.1 Prevent SQL Injection

Always use ORM or parameterized queries:

cursor.execute("SELECT * FROM users WHERE username = %s", [username])

5.2 Prevent Cross-Site Scripting (XSS)

  • Use Django’s auto-escaping in templates.

  • Enable content security policy (CSP) middleware:

pip install django-csp

Add CSP Headers:

MIDDLEWARE += ["csp.middleware.CSPMiddleware"]
CSP_DEFAULT_SRC = ["'self'"]
CSP_SCRIPT_SRC = ["'self'", "'unsafe-inline'"]

6. Configure Web Server Security

6.1 Use Gunicorn for Deployment

gunicorn --workers 3 myproject.wsgi

6.2 Harden Nginx Configuration

server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

7. Enforce Logging & Monitoring

7.1 Configure Django Logging

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "file": {
            "level": "ERROR",
            "class": "logging.FileHandler",
            "filename": "/var/log/django_errors.log",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["file"],
            "level": "ERROR",
            "propagate": True,
        },
    },
}

7.2 Use AWS CloudWatch for Logs

For AWS ECS or EC2, install CloudWatch Agent:

sudo yum install amazon-cloudwatch-agent

Configure awslogs:

[general]
state_file = /var/awslogs/state/agent-state

[/var/log/django]
file = /var/log/django_errors.log
log_group_name = django_logs

Start logging service:

sudo systemctl start awslogs

8. Set Up Firewalls & DDoS Protection

8.1 Restrict Database Access

For AWS RDS, create security groups:

aws ec2 authorize-security-group-ingress --group-id sg-12345 --protocol tcp --port 5432 --source-ip your-server-ip/32

For DigitalOcean, enable Cloud Firewalls.

For GCP, restrict database access using VPC firewall rules.

8.2 Use AWS WAF for DDoS Protection

aws waf create-web-acl --name MyWebACL

For Cloudflare, enable DDoS protection under Firewall Rules.


Final Thoughts

Securing Django in production requires configuring security settings, using cloud-native security tools, and continuously monitoring vulnerabilities. Whether deploying on AWS, DigitalOcean, GCP, or other platforms, follow these best practices to keep your application secure.

Feel free to reach me at AhmadWKhan.com to discuss your application’s security issues.

0
Subscribe to my newsletter

Read articles from Ahmad W Khan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ahmad W Khan
Ahmad W Khan