Rethink (?) how we handle security headers.

391 views
Skip to first unread message

Carlton Gibson

unread,
Jul 30, 2020, 5:06:56 AM7/30/20
to Django developers (Contributions to Django itself)
Hi.

(This is quite preliminary but...)

So we added support for Referrer-Policy in 3.0

This added the SECURE_REFERRER_POLICY setting.

We have a Someday/Maybe Permissions-Policy (was Feature-Policy).

Then a proposal for a new one Cross-Origin Opener Policy

> This can be implemented in a similar way to the Referrer-Policy header in the security middleware.

But are we going to continue to add settings along this line, one for every new header that comes up?

Maybe, but I feel like we might need to review how we handle such things.


One thought that has come up (here and elsewhere) is that it would be good if Middleware could be configured with parameters without having to subclass. I wonder if (I suspect) that has come up as an idea before?


Otherwise does anyone have thoughts on this issue? (Maybe we can just keep adding settings — we have a lot for *_COOKIE_* for example.) 


Thanks,
Carlton

Sci Mithilesh

unread,
Jul 30, 2020, 5:53:30 AM7/30/20
to django-d...@googlegroups.com
Your contact number send me I want a VIP site

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/f35d51f3-83d2-4ace-a288-daef7c31abe4o%40googlegroups.com.

Adam Johnson

unread,
Jul 30, 2020, 6:38:12 AM7/30/20
to django-d...@googlegroups.com
Sci - please stop posting on this mailing list about whatsapp etc. It's not appropriate.

Carlton - I have three thoughts re: security headers

1. I'm fine adding new settings for them. I think it's basically part of the territory. More headers are appearing, to support them we can use the same mechanism users are familiar with. Most projects should be able to go with the sensible default that Django provides.

2. On sensible defaults - the settings are currently bools but this requires users to set them to False for development and True for deployment on HTTPS. I think we could add a third value, 'on_https', that's the default. It could be true based upon request.is_secure(). This would reduce much of the user burden of these settings.

3. For new headers I think we could add a setting called e.g. ADD_HEADERS - a dict of keys to values that CommonMiddleware (or similar) could add to outgoing responses' headers. This would mean Django projects could use new security headers before Django supports them. I think the settings versions in SecurityMiddleware are still valuable since they provide defaults and allow per-view customization where reasonable.



--
Adam

Claude Paroz

unread,
Jul 30, 2020, 12:43:59 PM7/30/20
to Django developers (Contributions to Django itself)
By the way, while reviewing the SecurityMiddleware, I would suggest that the redirection part be moved to a different middleware.
http to https redirection should preferably be done at the Web server level, and for those doing that properly, they still pay for the unneeded (albeit small) overhead of the `SecurityMiddleware.process_request`.

> 3. For new headers I think we could add a setting called e.g. ADD_HEADERS - a dict of keys to values that
> CommonMiddleware (or similar) could add to outgoing responses' headers.

+1 to that proposal.

Claude

Adam Johnson

unread,
Jul 31, 2020, 1:25:08 PM7/31/20
to django-d...@googlegroups.com
I would suggest that the redirection part be moved to a different middleware.

I doubt this would have any noticeable performance impact on any application. I’d like to see profiling data before imposing such a change on users.

Also I find myself using the Django redirect with several different “serverlwss” deployment setups.

--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
--
Adam

Megan Huber

unread,
Aug 3, 2020, 1:16:39 PM8/3/20
to Django developers (Contributions to Django itself)

Hi Y'all, 

I suggested the addition of the COOP header. I don't have enough experience contributing to Django to know if the process of adding new headers should be streamlined. I am curious though if CORS or CORP support has ever been considered as a part of the security middleware. COOP is usually implemented with a header called Cross-origin Embedder Policy but it relies on CORS or CORP being set to a specific value. I know that currently Django projects use a middleware to handle CORS but we can't have a safe default for COEP set unless we know they are using this middleware. The middleware is also not a Mozilla repository. 

Maybe now is a good time to add support for CORS/CORP while we are adding many other security headers? I would be interested in contributing to this and could submit a new issue for it or do it as a part of the COOP ticket.

Another +1 to Adam's Add_Headers idea. This would be an effective way of keeping up with new security standards for concerned developers but would offer no protection to the average Django user.

Thanks,

Megan

James Bennett

unread,
Aug 19, 2020, 7:03:03 PM8/19/20
to django-d...@googlegroups.com
While I think Adam's right that adding one or two new settings
wouldn't be a problem, I do worry about the ongoing proliferation, and
it's a thing that I keep wanting to try to find the time to work on
but never actually succeed at.

Separate from the suggestion of a generic way to add headers on every
response, I've been leaning for a while toward the idea of
consolidating the security-header settings the way we've already
consolidated database and template settings. We can paint the bikeshed
whatever color, but suppose for sake of an example name we add a
SECURITY_HEADERS setting, with each one configured under its own key.
That would allow X-Frame-Options, content sniffing, HSTS,
Referrer-Policy, opener policy, and future stuff like CSP, built-in
CORS, etc. to all be defined in a single place that doesn't
proliferate a huge number of new settings, or require adding and
supporting a new setting every time a new one comes along (which, with
security headers, is about twice a week these days).

For now I think the best thing would be to accept the opener-policy
work as-is, then get consensus around a proposal for how future
security headers should be handled.

Adam Johnson

unread,
Aug 21, 2020, 12:53:33 PM8/21/20
to django-d...@googlegroups.com
A single SECURITY_HEADERS (or perhaps SECURITY) setting sounds good. Would love to get CORS into core too.

The Opener-Policy ticket has been marked as someday/maybe because the header is still not widely supported.

--
You received this message because you are subscribed to the Google Groups "Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.


--
Adam

Tim Graham

unread,
Jan 18, 2021, 8:30:32 PM1/18/21
to Django developers (Contributions to Django itself)
The proposal seems to be a setting of the form:

SECURITY_HEADERS = {
    'Strict-Transport-Security': 'max-age=60; includeSubDomains; preload',
    ...
}

Currently we have system checks to suggest setting SECURE_HSTS_SECONDS, SECURE_HSTS_PRELOAD, etc. Do you envision trying to keep these checks? It seems it'll be more complicated to parse and validate arbitrary string values instead of individual settings.

It seems some headers may still need special handling in SecurityMiddleware. For example, 'Strict-Transport-Security' is only set if request.is_secure().

Adam Johnson

unread,
Jan 19, 2021, 4:39:54 AM1/19/21
to django-d...@googlegroups.com
I'd imagined the setting would be more like a roll-up of the existing settings, so no need for parsing:

SECURITY_HEADERS = {
    "REFERRER_POLICY": "same-origin",
    "HSTS": {
        "PRELOAD": True,
        "SECONDS": 1_000_000,
     },
}




--
Adam

Tim Graham

unread,
Jan 19, 2021, 7:02:38 AM1/19/21
to Django developers (Contributions to Django itself)
I guess there have been a few different proposals here, but I imagined SECURITY_HEADERS would allow setting any security-related header without making changes in Django. If the setting is more than header/value pairs, then it seems this won't be the case.

Adam Johnson

unread,
Jan 19, 2021, 8:27:58 AM1/19/21
to django-d...@googlegroups.com
I don't see the need for adding a setting to add headers to outgoing responses. A middleware to do so is a handful of lines:

class SecurityHeadersMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response["Cross-Origin-Embedder-Policy"] = "require-corp"
        return response

Also there are many non-security related headers one might want to add, so having the ability to add them in a setting called SECURITY_HEADERS could be confusing for maintenance.



--
Adam

Tim Graham

unread,
Jan 24, 2021, 4:08:24 PM1/24/21
to Django developers (Contributions to Django itself)
If we go with something like:

SECURITY_HEADERS = {
    "REFERRER_POLICY": "same-origin",
    "HSTS": {
        "PRELOAD": True,
        "SECONDS": 1_000_000,
     },
}

should we have an empty dictionary as the default or a dictionary with defaults for all settings?

A drawback of an empty dictionary is that any code that wants to retrieve a setting has to do something like settings.SECURITY_HEADERS.get('REFERRER_POLICY', '<fallback value>') which means the fallback value is duplicated every place the setting is consulted.  Maybe a way to mitigate that is to add a "get_security_header()" function that retrieves from django.conf.settings (user-defined settings) and falls back to some defaults.

A drawback of a fully-populated default is that overriding it either requires users to redefine the entire dictionary or writing code like:

from django.conf.global_settings import SECURITY_HEADERS
SECURITY_HEADERS['REFERRER_POLICY'] = '...'

If users redefine the entire dictionary, then adding new SECURITY_HEADERS options in future Django versions will require something like "get_security_header()" since we won't be able to assume the key is there in projects upgrading from older versions.

I'm not so sure the benefit of consolidating to a single setting is worth this additional complexity.

chris.j...@gmail.com

unread,
Mar 26, 2021, 1:35:44 AM3/26/21
to Django developers (Contributions to Django itself)
I just came across this thread after seeing a related PR. Rather than a "get_security_header()" function, couldn't Django make the setting an object that is accessed by the user like a dict, but internally (underneath the hood) it tries from a user-defined dict and then falls back to a value in a Django-defined dict of defaults?

--Chris

Adam Johnson

unread,
Mar 26, 2021, 4:16:30 AM3/26/21
to django-d...@googlegroups.com
That would be counter to how all current dict based settings work, so I think it would be too surprising. 

--
Adam

chris.j...@gmail.com

unread,
Mar 26, 2021, 11:16:00 PM3/26/21
to Django developers (Contributions to Django itself)
On Friday, March 26, 2021 at 1:16:30 AM UTC-7 Adam Johnson wrote:
That would be counter to how all current dict based settings work, so I think it would be too surprising. 

I see. The django.conf.settings object should only include the raw settings data and not auxiliary objects that can depend on that data. In that case, my suggestion can be taken as an alternative API to a "get_security_header()" function. It can be an dict-like object imported from a module as opposed to being accessed through django.conf.settings. One advantage of a dict-like object over a single function is that has the ability to grow helper methods over time in addition to the dict-like accesses. That could be useful in different contexts. A precedent for this is the django.db.connections object, which is a ConnectionHandler object constructed from settings and that supports some dict-like operations.

--Chris
Reply all
Reply to author
Forward
0 new messages