Why does my django allow downloading files outside of static folder?

Viewed 108

My django project allows downloding images from static folder:

http://0.0.0.0:8001/static/img/login.png

This makes sense.

But what concerns me is that it also allows me downloading files even outside of the static folder such as:

http://0.0.0.0:8000/logs/result.log

or even worse:

http://0.0.0.0:8000/users/models.py

All my local files can be downloaded via clients.

This is a high security vulnerability. No matter what is the permission of my folders on my Linux server. I do not have such items in my path and I do not expect these paths being served.

How can I fix this?



BASE_DIR = Path(__file__).resolve().parent.parent

DEBUG = True

INSTALLED_APPS = [
        'comments',
        'home',
        'users',
        'articles',
        'notifications',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.messages',
        'django.contrib.sessions',
        'django.contrib.staticfiles',
        'static_precompiler',
        'django_markup',
        'django_minify_html',
        'django_rename_app',
    ]

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',
        'django_minify_html.middleware.MinifyHtmlMiddleware',
    ]

ROOT_URLCONF = 'profplus.urls'

TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': ["templates"],
            '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 = 'profplus.wsgi.application'


STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_FINDERS = [
        'django.contrib.staticfiles.finders.FileSystemFinder',
        'django.contrib.staticfiles.finders.AppDirectoriesFinder',
        'static_precompiler.finders.StaticPrecompilerFinder',
    ]


4 Answers

According to the official docs: how to manage static files:

In addition to these configuration steps, you’ll also need to actually serve the static files.

During development, if you use django.contrib.staticfiles, this will be done automatically by runserver when DEBUG is set to True (see django.contrib.staticfiles.views.serve()).

This method is grossly inefficient and probably insecure, so it is unsuitable for production.

That being said, no-one cares about the security of serving static files in Django and you shouldn't be using it in production. Use a dedicated server to serve static files, how to deploy static files in production

There seems to be a typo here?

TEMPLATES = TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ["templates"],
        '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',
            ],
        },
    },
]

It should just have one word TEMPLATES and DIRS should be an ABSOLUTE path like this:

 TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates"),],
        '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',
            ],
        },
    },
]

In production, you can use Nginx to deploy your app and if set up correctly, all the paths that are not declared in urls.py should be denied.

Hope that helps.

The builtin static file server is not intended for production use, so it makes no effort to deal with security issues like the one you mention. Normally, static files are not served by any Python application -- static files are typically offloaded to a server or service specialized for serving static files, such as a traditional webserver (Apache, NGINX, etc) or a CDN (CloudFlare, CloudFront, Azure Frontdoor, etc). This is described in the django docs.

If you don't have a CDN and don't want to use a separate server for static files and you must use your Python application to serve static files, you can use the whitenoise middleware in your Django project instead.

Install the package:

pip install whitenoise

Then add it to your project:

MIDDLEWARE = [
    # ...
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # ...
]

And remove the STATICFILES_FINDERS setting you have currently.

Check the whitenoise docs for more information and configuration options.

Related