DjangoCMS: disable login via http, force https - django

Our DjangoCMS site is accessible via http and https.
Anonymous usage via http is ok. But I want to disable logins via http.
Is there a way to force the usage of https as soon as the user wants to login?
Even the login-page (with username and password fields) should not be available via http.
Background: I don't want the password to go over the wire unencrypted.
Update: The site gets hosted on an apache web server.

As I already mentioned it in the comments I strongly suggest you to NOT only serve the login page via https.
Doing so just hides the fact that for example session information and authentication data is still transfered on the other requests unencrypted via http. Your site will not be secure at all.
You're just pseudo-securing stuff so it's fancy to somebodys eye. It's just like using the password 12345.
So please serve your website over https to the user. A small guide, for nginx or apache2, on how to redirect your traffic from http to https can be found here:
Redirecting to SSL using nginx
Force redirect to SSL for all pages apart from one

As you've accepted in the comments, pushing all traffic to HTTPS is the best solution these days. If you only authenticate via SSL, you'll need to consider encryption/security in everything you write moving forward rather than it just being the default.
So in your server config, force all traffic to port 443. Assuming you're using apache you'd do this;
<VirtualHost *:80>
ServerName www.example.com
Redirect / https://www.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName www.example.com
# ... SSL configuration goes here
</VirtualHost>
Then in your Django settings turn on secure cookies;
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
As a side note, Django has a setting to redirect HTTP traffic to HTTPS which is SECURE_SSL_REDIRECT, but if you're doing this at the apache/nginx level you don't need to worry. For some further reading on SSL/HTTPS have a look here; https://docs.djangoproject.com/en/1.11/topics/security/#ssl-https

Django request has a is_secure()
You can check it in the view and redirect if not is secure:
if not request.is_secure():
return redirect("https://www.yourdomain.com/login")

#user1 has the logic right and you should serve your site through https in order to have it safe.
But in order to answer your exact question:
As shown here: How to know using Django if server is secure (uses https) and as #Zartch mentions in his answer, you should use the is_secure() HTTPRequest method to check if your request is coming through https.
In order to use is_secure() you need to check an incoming request
to your views:
def my_login_view(request):
if request.is_secure():
Do loggin
else:
Don't login
Now you can protect your other views with the login_required decorator:
#login_required(login_url='/your/login/url')
def protected_view(request):
...
Personal suggestion: I use Django Rest Framework extensively and I would suggest it, in your case, because it has an isAuthenticatedOrReadOnly permission class which you will like:
class MyLoginView(ObtainAuthToken):
"""
Login view for token-based authentication
"""
def post(self, request, *args, **kwargs):
if request.is_secure():
super().post(request, *args, **kwargs)
else:
Probably some redirect goes here...
Then in any other view or class-based view:
class MyOtherView(generics.GenericAPIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
...
The above will ensure that your users can login only through https and if they are not logged in they will only see a read-only view.
Give it a try if you like.

You didn't accept an answer yet, so I thought, that the answers might not match your question well enough.
Anonymous usage via http is ok. But I want to disable logins via http.
As I understand, you have the following use case:
Some person visits your website as anonymous user, the used protocol is http.
This person opens the login form and logs in, the form is sent through https.
The logged in user continues browsing your website through https.
If the login form is routed to the secure version your user will continue browsing your website in https mode (as long as all other links are agnostic to the used protocol).
<form action="https://your.domain/path/to/login/" method="post">
If the application you're using to authenticate users is sanely written, you can configure your settings.LOGIN_URL, else you'll probably need to do it in the template form.
I recommend allowing session cookies to be sent only through https, using settings.SESSION_COOKIE_SECURE.
With this setting, the user will still be able to make http requests, but the session cookie will not be sent (and the user will be treated as an anonymous user).
Please, make sure that your login view accepts only secure post requests (or else the user may send the password through non https protocol).
This can be achieved either in the Django code (like John Moutafis suggests with his MyLoginView) or at production server level by rejecting non https post requests for the login URL.
--
Could you provide access to the repository with the code?

You may use one or a combination of:
htaccess file, take a look here
webserver level, depend of server type: Apache, Nginx, IIS ?...
django server level, take a look here, and host header validation or SSL/HTTPS

Related

In Django is it possible to obfuscate or hide client IP from logging on certain pages?

We have a need to allow a user of our internal Django site to send an anonymous email via a contact form to an internal email box. I have all of the framework in place and the messages arrive and are not obviously traceable. However, the Django logs do record the IP address along with a timestamp of all requests.
I am curious if it would be possible to some how hide or perhaps proxy the client IP when the form is submitted to help protect their anonymity?
I have found many articles on how to get the client IP but my searches have not hit upon anything that does the reverse.
I could perhaps alter the Django Logging format to exclude the client IP but it would be good to leave it on for the rest of the site for debugging etc.
thank you in advance.
The answer for me was to modify the Apache CustomLog entry to exclude specific content from being logged in the Access log based on the URI. I made an entry similar to the following in my vhost config file based on numerous examples and the Apache Logging documentation.
SetEnvIf Request_URI "^/staff/contact" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog

django: SECURE_PROXY_SSL_HEADER requires referer

We have a django app accessed via SSL (i.e. with https). When we went
to the admin site and it was redirected to admin/login/?next=/admin/
because we were not logged in, the https was not carried over and the
request failed. I added
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
to my settings and then the admin redirect worked. But we have some
clients that access the site with curl or python requests and after
adding that all their existing code broke. They now all have to add a
referer to all their requests for them to work.
My question is, is there a way to make the admin redirect work but not
require all the other requests to have a referer? A non redirected
request from the browser works, and that doesn't have a referer, so
why is it required on the curl requests?
I found this:
https://code.djangoproject.com/ticket/16870
which I guess somewhat answers my question.

Redirecting from HTTP to HTTPS w/ Simple Auth

I was hoping to get some recommendations on how to approach redirecting users from HTTP to HTTPS using an ember initializer with ember-simple-auth.
`import ENV from 'cio/config/environment'`
SSLInitializer =
name: 'ssl'
before: 'simple-auth-cookie-store'
initialize: (container, application) ->
application.deferReadiness()
# Redirect if hitting HTTP and SSL is enabled
if ENV.SSL and window.location.protocol is "http:"
window.location.href = "https:" + window.location.href.substring(window.location.protocol.length)
return false
application.advanceReadiness()
`export default SSLInitializer`
But it seems that the cookie gets invalidated even when the if statement evaluates to true. I've tried several things, including:
before: 'simple-auth'
before: 'store'
application.destroy() within the if statement, before the window.location.href is set
From what I can tell, after debugging. The app does redirect to HTTPS, but then the cookieName is not found in document.cookie. (https://github.com/simplabs/ember-simple-auth/blob/master/packages/ember-simple-auth-cookie-store/lib/simple-auth-cookie-store/stores/cookie.js#L154)
Before this method worked because we had simple snippet in the index.html, but w/ CSP we'd like to keep it in an initializer. Any recommendations?
Thanks!
You really should be forcing a redirect from HTTP to HTTPS from the server as doing it from the client does not add any real security.
Think about it, the user has downloaded the application to their browser from an insecure endpoint and from then on nothing can be trusted. Even a server based redirect is problematic since it relies on the redirect advice from an untrusted endpoint. Users should really be accessing things from an initial trusted starting point otherwise all bets are off. This is known as the secure referral problem and will likely never be solved because of the business model behind SSL certificates.
You also shouldn't really trust cookies from the untrusted HTTP domain in the trusted HTTPS domain unless you have a way to authenticate those cookies on the client. Sharing of cookies between HTTP/HTTPS is covered in RFC 2109 (Section 4.2.2 Set-Cookie Syntax).
This means:
A cookie set with "Secure" will be available only on HTTPS
A cookie set without "Secure" will be available on either HTTP or HTTPS.

Django request.is_secure returns wrong value for redirected methods

I am trying to write a ssl redirection utility for django apps (https://bitbucket.org/yilmazhuseyin/django-sslredirector). My problem is when I redirect pages from http to https, I cannot understand that I am on secure connection ( when I call request.is_secure it returns false). I think there is a hack for this , somehow called Webfaction that I cannot really get how it works. here is the is_secure method for webfaction case
def _is_secure(self, request):
if request.is_secure():
return True
#Handle the Webfaction case until this gets resolved in the request.is_secure()
if 'HTTP_X_FORWARDED_SSL' in request.META:
return request.META['HTTP_X_FORWARDED_SSL'] == 'on'
My problem is when I redirect my pages from http to https, request.is_secure method still returns false (event though I am on https) and I constantly redirect my pages to https.
Is there any way to understand if I am just redirected from https?
The best source I could find is this http://djangosnippets.org/snippets/880/ and it is not working for me
If your traffic is going through some kind of proxy it is possible that the fact that you are using SSL will be hidden. However, the proxy will usually set some kind of HTTP header (or you can configure it to do so). One option is HTTP_X_FORWARDED_SSL. Heroku sets HTTP_X_FORWARDED_PROTO to https if you are using https.

django: Is it possible to log a user into a subdomain, from another domain?

The thing is. I have one django app serving different sites.
site1.myapp.com
site2.myapp.com
The users login via a 3rd party SSO system which is then redirected(inkl. a valdiation POST) to https://myapp.com/auth/
However. since my users all belong to only 1 "site" i would like myapp.com/auth/
to log the user into the relevant site, ex. site1.myapp.com or site2.myapp.com and then redirect them to that siteā€¦
Is this at all possible?? or should i go about this in a totally different way? :)
I should mention that when it comes to the general usage of the app I have subdomain middleware to ensure that the users always only visit the subdomain(and data) that their account is valid for.
The reason I want to use subdomains is to make it simple for the users to remember their account url, while maintaining the pros of having to maintain just one django app.
thanks. hope you can help :)
kind regards.
pete
I know this question is old, but since Google brought me here I'll add these links
This answer touches on (A) authentication across subdomains and (B) detecting which subdomain is in use to potentially redirect the user
A.1. If you want to allow all (wildcard) subdomains
*.myapp.com, this is achieved by adding one line to settings.py:
SESSION_COOKIE_DOMAIN=".myapp.com"
Detailed here (SO, 2009), here (SO, 2010) and in Django docs
Note: login now won't work on localhost, so you have two choices if
you need to log in and out on localhost:
1: comment out that line in settings.py, or
2: amend your /etc/hosts file to include the following:
127.0.0.1 localhost
127.0.0.1 dev.myapp.com
Now you can visit dev.myapp.com in your browser, and it'll actually be talking to 127.0.0.1, not your live
website. (Now, across dev.myapp.com, site1.myapp.com,
site2.myapp.com and myapp.com, if you log in/out of one, you'll be
logged in/out of them all.)
A.2. If you want to allow cross-authentication between just those two subdomains, i.e., they won't be logged into site3.myapp.com, then it gets a bit more complicated
B. To view the subdomain being used
There are fancier packages to manage subdomains in django, but you could just look crudely at request.META['HTTP_HOST']:
try:
http_host = request.META['HTTP_HOST']
# alternative: http_host = request.get_host()
except KeyError:
http_host = None
print "Can't find HTTP_HOST"
if http_host and '.myapp.com' in http_host:
subdomain = http_host.split('.myapp.com')[0]
else:
subdomain = ''
Then check if you're happy with the request.user using this subdomain.
Use something like HttpResponseRedirect to send them to a different subdomain if you like. If you've done A.1 or A.2 above, in your app's eyes, they're the same user (already logged in) in the new subdomain.myapp.com after being redirected (they don't have to log in again).
Example: if a user creates an account with ireland.myapp.com and you want to keep them always on that site, then when they try to visit usa.myapp.com, they'll still be logged in, you can identify them and send them back to ireland.myapp.com (fictitious example, not a metaphor for immigration!)
In Django you have the notion of sites. You can create your own log in view. If it's not enough, you can create your own authentification backend.