I'm writing a Django 1.3 view method which requires TLS/SSL to be used. I want to entirely drop the connection if an HttpRequest is received without using TLS/SSL and NOT return any kind of response. This is for security reasons.
Currently I am returning a response like so:
def some_view(request):
if not request.is_secure():
return HttpResponse(status=426)
...
However, returning 426 - Upgrade Required
poses a couple of problems:
- It's part of a proposed standard from May 2000 (RFC 2817), and is not an official HTTP standard.
- The HttpResponse is open to a man-in-the-middle (MITM) attack. As mentioned in the comments here, if the server returns any type of response to the client without a TLS/SSL connection first being established, a MITM could hijack the response, alter it to re-direct elsewhere, and deliver the malicious re-direct response to the client.
Having the server re-direct from a HTTP URI to a HTTPS URI is open to the same MITM attack as noted above.
So, how can you entirely drop a connection inside a Django 1.3 view method without returning any type of HttpResponse?
As I was saying in this answer, I'm generally against the use of automatic redirections from
http://
tohttps://
for the reasons you're mentioning. I would certainly not recommend resorting only to bulkmod_rewrite
-style redirections for securing a site.However, in your case, by the time the request reaches the server, it's too late. If there is a MITM, he has done his attack (or part of it) before you got the request.
The best you can do by then is to reply without any useful content. In this case, a redirection (using 301 or 302 and the
Location
header) could be appropriate. However, it may hide problems if the user (or even you as a developer) ignores the warnings (in this case, the browser will follow the redirection and retry the request almost transparently).Therefore, I would simply suggest returning a 404 status:
http://yoursite/
andhttps://yoursite/
are effectively two distinct sites. There is no reason to expect a 1:1 mapping of all resources from the URI spaces from one to the other (just in the same way as you could have a completely different hierarchy forftp://yoursite/
).http://
should be considered as broken. Don't make it work automatically. Having a 404 status for a resource that shouldn't be there is fine. In addition, returning an error message when there is an error is good: it will force you (or at least remind you) as a developer that you need to fix the page/form/link that led to this problem.Dropping the connection is just a bonus, if you can do this with this framework: it will only be really useful if it can be sent asynchronously by the server (before the client has finished sending the request), if the browser can read it asynchronously (in which case it should stop sending immediately when there is an error) and if the MITM attacker is passive (an active MITM could stop the response to go back through the client and make sure the client sends all the request by consuming it with its own "proxy", whether or not the server has dropped the connection). These conditions can happen, but fixing the problem at the source is still better anyway.
If you are using Nginx as your front-end proxy then you can use another non-standard HTTP status code 444 which closes the connection without sending any headers. http://wiki.nginx.org/HttpRewriteModule#return It would require that Nginx know enough about which urls to deny on HTTP.