Django 3.1 gotcha: Referrer Policy has a new default, and it might break iframes and links
TL;DR:
If you have a Django application, and you've upgraded to 3.1 or later, and you make extensive use of iframe
s or the referrer
attribute in some way, you will want to think about setting SECURE_REFERRER_POLICY
so that you aren't unwittingly walking into the new default of same-origin
.
Backstory
I'm sharing a hard lesson that we learned on a project at work, having to do with upgrading to Django 3.1.
This project involves at it's core experience an iframe
. Inside that iframe
is another application that depends on the parent frame's referrer
attribute.
What is a "referrer"?
A "referrer" is an attribute that a parent page shares with either an <iframe>
or a link that let's that other frame/page know "this URL referred the user to you".
If Site A has a link to Site B, and a user clicks on that link, when they arrive at Site B, Site B would know what URL sent the user to them.
The same thing applies to <iframes>
- if Site A has an iframe to Site B, Site B would then know what was referring to it.
What is "referrer-policy", then?
You may or may not want to share that information. By setting a referrer policy, you are declaring in what circumstances you want to share the referrer attribute. There are lots of different options that you can specify.
As of Django 3.0, the framework allows you to set this policy.
What changed in Django 3.0?
Django 3.0 added a new setting - SECURE_REFERRER_POLICY
. It defaulted to None
initially.
With that set to None
, or in it's absence (which was the case in Django 2.x and below), no policy is set.
Browsers then, when they get a response in this situation, default to the policy no-referrer-when-downgrade
- more on what that means in a minute.
What changed in Django 3.1, then?
In Django 3.1 SECURE_REFERRER_POLICY
got a new default - same-origin
. See the note here.
Because we had never explicitly set SECURE_REFERRER_POLICY
, that new policy went into effect in our parent application.
Why is "same-origin" problematic?
Well, same-origin
isn't problematic. In fact, it does make a reasonable default. It's more secure.
When the parent has the policy of same-origin
, it will only share the referrer attribute with other sites that have, well, the same origin. The origin is the full host URL, including subdomain. Subdomains will not share referrers when the policy is "same-origin".
In our case, we had our parent application at www.app.tld
, and the second app at second.app.tld
. The policy of same-origin
means that the referrer
would not get shared in this case, which lead to our broken second app.
How did you fix it?
The fix was pretty easy, once we got to the bottom of the issue. We added this one line to our application's settings.py
:
SECURE_REFERRER_POLICY = "no-referrer-when-downgrade"
Which explicitly tells the application to use no-referrer-when-downgrade
as the policy. This allowed us to share referrer
between subdomains, and thus restored our second application's functionality.
no referrer when downgrade? huh?
This policy basically says "don't share the referrer
when the child/link has a lesser security protocol, but otherwise always share the referrer
". This is the default for the web.
That means if a parent site is https://site.a
and it links to http://site.b
- a referrer
will not be passed. In all other situations (and in our case, most importantly, across subdomains), the referrer
will be passed.
Conclusion
If your app relies on iframe
s, or links that rely on a referrer
- make sure that you have thought through what your referrer-policy
is.
I suggest you review the MDN article on what the possible values are and choose what makes sense for your project.
Previous: A Javascript Component Pattern