Here's a demo on a site with multiple settings.ALLOWED_HOSTS values:
''We start the server.''
{{{
[orlnub123@orlnub123 mysite]$ ./manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
March 24, 2019 - 05:38:46
Django version 2.1.7, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
}}}
''Then send a normal request. The referer header here is important;
without it the email won't get sent.''
{{{
[orlnub123@orlnub123 mysite]$ curl -H 'Referer:
http://example.com/referrer' -w '\n' localhost:8000
<h1>Not Found</h1><p>The requested resource was not found on this
server.</p>
}}}
''We get the email; everything looks good.''
{{{
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Broken link on localhost:8000
From: root@localhost
To: jo...@example.com
Date: Sun, 24 Mar 2019 05:38:49 -0000
Message-ID:
<155340592942.14004....@orlnub123.localdomain>
Referrer: http://example.com/referrer
Requested URL: /
User agent: curl/7.64.0
IP address: 127.0.0.1
-------------------------------------------------------------------------------
[24/Mar/2019 05:38:49] "GET / HTTP/1.1" 404 77
}}}
''We send another request; this time with a spoofed host header.''
{{{
[orlnub123@orlnub123 mysite]$ curl -H 'Referer:
http://example.com/referrer' -H 'Host: example.com' -w '\n' localhost:8000
<h1>Not Found</h1><p>The requested resource was not found on this
server.</p>
}}}
''Oh no! The subject contains our spoofed header and presents the link as
an internal one.''
{{{
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Broken INTERNAL link on example.com
From: root@localhost
To: jo...@example.com
Date: Sun, 24 Mar 2019 05:38:59 -0000
Message-ID:
<155340593990.14004....@orlnub123.localdomain>
Referrer: http://example.com/referrer
Requested URL: /
User agent: curl/7.64.0
IP address: 127.0.0.1
-------------------------------------------------------------------------------
[24/Mar/2019 05:38:59] "GET / HTTP/1.1" 404 77
}}}
I've fixed it by replacing the `request.get_host()` call with
`get_current_site(request).domain`. This approach falls back to using
`request.get_host()` internally on sites that don't use the sites
framework. It isn't perfect, but it replicates what many other components
do.
--
Ticket URL: <https://code.djangoproject.com/ticket/30285>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* status: new => closed
* resolution: => wontfix
Comment:
Grrr, happy to discuss options but, I think we have to say `wontfix` here:
we can't introduce a dependency on `contrib.sites` in `middleware.common`.
If it weren't for that, I'd probably say, "It's a bit tenuous, but OK, if
you're going to fix it". So, question, any ''fix'' that doesn't depend on
the sites framework?
(Given that the only ''spoof-able'' domains must be in `ALLOWED_HOSTS` and
the affected function is `mail_managers()` I can't see that we need to
take Herculean measures here...)
--
Ticket URL: <https://code.djangoproject.com/ticket/30285#comment:1>