Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django error reporting emails: env vars leak info

Django's builtin capability of emailing admins upon errors (see https://docs.djangoproject.com/en/dev/howto/error-reporting/) is quite handy.

However, these traceback emails include a full dump of environment variables.

And as advised in the django docs & elsewhere (e.g. https://docs.djangoproject.com/en/dev/howto/deployment/checklist/) I've moved some secrets/keys/passwords into environment variables as a simple way to keep them away from the codebase & vary them across deployments. Unfortunately this means that when there's a crash report these secrets get sent in the clear to a set of email accounts. Not a good practice.

The django ExceptionReporter has basic filtering to pull out "dangerous or offensive" settings, so e.g. the value of any item in settings.py whose name contains the strings "pass" or "key" will be replaced with ****s. Thus a secret key in settings.py gets edited out. But this filter is not applied to environment variables, which appear in both the Traceback->Local vars->request and Request Information->Meta sections of these error reports.

Obviously there are other ways to manage secrets but the unix environment is a pretty common solution for small sites where creating a more complex configuration system isn't warranted.

It also seems problematic that these two practices, both recommended in the basic django docs, are unsafe when applied together.

Emailing around site debug information always carries some risk of leaking information, but this seems like a significant omission that could be addressed by expanded filtering, perhaps controlled by some setting.

Has anyone already patched this (presumably expanding the filtering in django/views/debug.py) for their deployment and/or submitted a patch to the django team? Or am I missing some other obvious way to address this?

like image 550
geewiz Avatar asked Dec 10 '14 21:12

geewiz


3 Answers

OK, missed this in my previous checking, but apparently the django team had approximately this bug logged & closed it as will-not-fix 6 years ago:

https://code.djangoproject.com/ticket/7472

I will take it up with them, since I believe that django has made substantial security progress in the intervening time and now may want to, and have some simple ways to, address this. :)

In the meantime, if you use this email-the-admins function please be cognizant of the risk. If you send these emails then I would strongly urge you to leave or place all secrets/passwords/keys/certs/etc in python config files, and to ensure you are scrubbing the (unix) environment that is passed to your django web service.

like image 146
geewiz Avatar answered Nov 14 '22 21:11

geewiz


I ran into the same issue, and addressed it by creating custom Middleware, as described in the Django docs. This approach assumes that you know the names of the env variables that you want to hide from error pages / emails, and that you don't actually need these variables present in every request. You can then filter them out from the request.META dictionary before the error response gets generated:

class RequestSafetyMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        request.META.pop('TOP_SECRET', None)
        response = self.get_response(request)
        return response

I hope this helps! I wish Django applied the same safety obfuscation rules to env variables as it applies to settings, so this would not even be an issue.

like image 44
Sergei Krupenin Avatar answered Nov 14 '22 20:11

Sergei Krupenin


In the case of using mod_wsgi with Apache, environment variables (set through the Apache SetEnv directive or otherwise) are passed to the application function in the first environ argument.

In order to be able to access these environment variables in settings.py (or elsewhere), it is convenient to copy them to os.environ, using e.g.

os.environ['TOP_SECRET'] = environ['TOP_SECRET']

After this, environ is passed to django.core.handlers.wsgi.WSGIHandler (via django.core.wsgi.get_wsgi_application) where it eventually makes it's way to the error reports.

The TOP_SECRET key doesn't need to be retained in environ after it is copied to os.environ, so changing the line above to os.environ['TOP_SECRET'] = environ.pop('TOP_SECRET', '') removes it from the error reports.

Putting it all together, my wsgi.py file is as follows:

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "App.settings")

env_variables_to_pass = ['TOP_SECRET']

def application(environ, start_response):
    # pass the WSGI environment variables on through to os.environ
    for var in env_variables_to_pass:
        os.environ[var] = environ.pop(var, '')
    return get_wsgi_application()(environ, start_response)

This means that the required environment variables are available in os.environ where needed, but they don't show up in the error reports.

It's possible that I've missed something here, but this does seem to be working for me. If there is a reason not to do this, please post a comment. It may be safer to create a copy of the environ dictionary first, i.e. my_environ = copy.deepcopy(environ), then use that instead of environ directly.

Note also that other sensitive variables (e.g. passwords in POST requests) should be filtered instead.

like image 1
zelanix Avatar answered Nov 14 '22 20:11

zelanix