in— layout: default —
Set up and Harden a Django Web Server connected to a SQL Database with 2FA | Python, Django, Web Servers, IAM, Hardening
In this hands-on activity from J. P. Morgan & Chase Co. Cybersecurity Job Simulation
I simulated being a security analyst for the financial company tasked with setting up and securing a Django Web Server with two-factor authentication
(2FA).
Steps to set Up the Django Server
1.Set up a virtual environment called ‘forageenv’ using Python venv
module to host the web server.
2.Unziped the mysite.zip
folder in the virtual environment. The files contained are the following .py
files (each one is explained at the head):
asgi.py
"""
ASGI config for mysite project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_asgi_application()
settings.py
"""
Django settings for mysite project.
Generated by 'django-admin startproject' using Django 3.1.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'x12w#6*^8y#i!@xbmgf-o)3k1tyz-v^73k$v!cl+^$dfc1s4i8'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
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',
]
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
urls.py
"""mysite URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django_otp.admin import OTPAdminSite
admin.site.__class__ = OTPAdminSite
urlpatterns = [
path('admin/', admin.site.urls),
]
wsgi.py
"""
WSGI config for mysite project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()
3.Installed the requierements.txt
using the Windows cmd
:
C:/users/redacted/forageenv>pip install -r requirements.txt
django
django-otp
qrcode
4.Synchronized the database db.sqlite3
with the existing configuration, or if it doesn’t exist, creating it using the following cmd
:
C:/users/redacted/forageenv>python manage.py migrate
6.Created the site admin user:
C:/users/redacted/forageenv>python manage.py createsuperuser
7.Established a username, email, and password when prompted:
C:/users/redacted/forageenv>python manage.py createsuperuser
Username (leave blank to use 'admin'): admin
Email address: admin123@example.com
Password: **********
Password (again): **********
Superuser created successfully.
8.Ran the server:
C:/users/redacted/forageenv>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
November 7, 2024 - 12:00:00
Django version 4.2.6, using settings 'myproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
Steps to Secure the web server using 2FA
1.Stopped the server from running by using ctrl+c
:
^C
Shutting down server...
2.Followed the Django OTP installation docs and modified settings.py
, using the otp_totp
plugin. This plugin adds two-factor authentication
by adding a one time passwords sent to the email that must be provided in order to access as admin. Following the instructions I modified the Application Definition
part of the code:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
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',
]
The additon of two-factor authentication
is presented in this snippet of code:
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_otp', # Plugin installed
'django_otp.plugins.otp_totp', # Plugin installed
'django_otp.plugins.otp_hotp', # Plugin installed
'django_otp.plugins.otp_static', # Plugin installed
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware', # Properly added two-factor authentication middleware with the line 'django_otp.middleware.OTPMiddleware';
'django.contrib.messages.middleware.MessageMiddleware', # Middleware is a software layer that connects the operating system to applications, data, and users,
'django.middleware.clickjacking.XFrameOptionsMiddleware', # and provides common services and capabilities.
]
3.Applied the migrations again after modification:
C:/users/redacted/forageenv>python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying sessions.0001_initial... OK
Migrations applied successfully.
4.Ran the server again:
C:/users/redacted/forageenv>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
November 07, 2024 - 14:23:50
Django version 3.2, using settings 'myproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
5.Used OTPAdminSite
from django_otp
for the admin site:
C:/users/redacted/forageenv>python -c "from django_otp.admin import OTPAdminSite; import admin; admin.site.__class__ = OTPAdminSite"