web-dev-qa-db-fra.com

Comment déployer un site HTTPS uniquement, avec Django / nginx?

Ma question initiale était de savoir comment activer HTTPS pour une Django , et la seule réponse, a recommandé que je - fasse le site entier en HTTPS- seulement.

Étant donné que j'utilise Django 1.3 et nginx, quelle est la bonne façon de créer un site HTTPS uniquement?

La seule réponse mentionnée ne solution middleware , mais avait la mise en garde:

Django ne peut pas effectuer une redirection SSL tout en conservant les données POST. Veuillez structurer vos vues afin que les redirections ne se produisent que pendant les GET.

Une question sur Server Fault à propos de nginx réécriture sur https , a également mentionné des problèmes avec la perte de données des POST, et je ne connais pas suffisamment nginx pour déterminer le fonctionnement de la solution.

Et la recommandation d'EFF de passer en HTTPS uniquement , note que:

L'application doit définir l'attribut Secure sur le cookie lors de sa configuration. Cet attribut indique au navigateur d'envoyer le cookie uniquement via un transport sécurisé (HTTPS), jamais non sécurisé (HTTP).

Les applications comme Django-auth ont-elles la possibilité de définir des cookies comme sécurisés? Ou dois-je écrire plus de middleware?

Alors, quelle est la meilleure façon de configurer la combinaison de Django/nginx pour implémenter HTTPS uniquement, en termes de:

  • sécurité
  • conservation des données POST
  • cookies correctement gérés
  • interaction avec d'autres Django applications (telles que Django-auth), fonctionne correctement
  • tout autre problème que je ne connais pas :)

Modifier - un autre problème que je viens de découvrir, lors du test de plusieurs navigateurs. Dites que j'ai l'URL https://mysite.com/search/, qui a un formulaire/bouton de recherche. Je clique sur le bouton, traite le formulaire en Django comme d'habitude, et fais un Django HttpResponseRedirect to http://mysite.com/search?results="foo". Nginx redirige cela vers https://mysite.com/search?results="foo", comme voulu.

Cependant - Opera a un flash visible lorsque la redirection se produit. Et cela se produit à chaque recherche, même pour le même terme de recherche (je suppose que https ne cache pas vraiment :) Pire, quand je le teste dans IE, je reçois d'abord le message:

Vous êtes sur le point d'être redirigé vers une connexion qui n'est pas sécurisée - continuer?

Après avoir cliqué sur "oui", ceci est immédiatement suivi de:

Vous êtes sur le point d'afficher les pages via une connexion sécurisée - continuer?

Bien que le deuxième avertissement IE a une option pour le désactiver - l'avertissement d'abord ne le fait pas, donc chaque fois que quelqu'un fait une recherche et est redirigé vers un résultat page, ils reçoivent au moins un message d'avertissement.

38
John C

Pour la 2ème partie de la réponse de John C, et Django 1.4 + ...

Au lieu d'étendre HttpResponseRedirect, vous pouvez remplacer le request.scheme Par https. Parce que Django est derrière le proxy inverse de Nginx, il ne sait pas que la requête d'origine était sécurisée.

Dans vos paramètres Django, définissez le paramètre SECURE_PROXY_SSL_HEADER :

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

Ensuite, vous avez besoin de Nginx pour définir l'en-tête personnalisé dans le proxy inverse. Dans les paramètres du site Nginx:

location / {
    # ... 
    proxy_set_header X-Forwarded-Proto $scheme;
}

De cette façon, request.scheme == 'https' Et request.is_secure() renvoie True. request.build_absolute_uri() renvoie https://... et ainsi de suite ...

52
yprez

Voici la solution que j'ai trouvée jusqu'à présent. Il y a deux parties, la configuration de nginx et l'écriture de code pour Django. La partie nginx gère les requêtes externe, redirigeant les pages http vers https, et le Django code gère interne Génération d'URL avec un préfixe http. (Au moins, ceux résultant d'une HttpResponseRedirect()). Combiné, il semble bien fonctionner - pour autant que je sache, le navigateur client ne voit jamais une page http que les utilisateurs n'ont pas saisie eux-mêmes.

Première partie, configuration nginx

# nginx.conf
# Redirects any requests on port 80 (http) to https:
server {
    listen       80;
    server_name  www.mysite.com mysite.com;
    rewrite ^ https://mysite.com$request_uri? permanent;
#    rewrite ^ https://mysite.com$uri permanent; # also works
}
# Django pass-thru via uWSGI, only from https requests:
server {
    listen       443;
    ssl          on;
    ssl_certificate        /etc/ssl/certs/mysite.com.chain.crt;
    ssl_certificate_key    /etc/ssl/private/mysite.com.key;

    server_name  mysite.com;
    location / {
        uwsgi_pass 127.0.0.1:8088;
        include uwsgi_params;
    }
}

Deuxième partie A, divers paramètres de cookies sécurisés, à partir de settings.py

SERVER_TYPE = "DEV"
SESSION_COOKIE_HTTPONLY = Vrai
SESSION_COOKIE_SECURE = Vrai
CSRF_COOKIE_SECURE = True # actuellement uniquement dans la branche Dev de Django.
SESSION_EXPIRE_AT_BROWSER_CLOSE = Vrai

Deuxième partie B, Django code

# mysite.utilities.decorators.py
import settings

def HTTPS_Response(request, URL):
    if settings.SERVER_TYPE == "DEV":
        new_URL = URL
    else:
        absolute_URL = request.build_absolute_uri(URL)
        new_URL = "https%s" % absolute_URL[4:]
    return HttpResponseRedirect(new_URL)

# views.py

def show_items(request):
    if request.method == 'POST':
        newURL = handle_post(request)
        return HTTPS_Response(request, newURL) # replaces HttpResponseRedirect()
    else: # request.method == 'GET'
        theForm = handle_get(request)
    csrfContext = RequestContext(request, {'theForm': theForm,})
    return render_to_response('item-search.html', csrfContext)

def handle_post(request):
    URL = reverse('item-found') # name of view in urls.py
    item = request.REQUEST.get('item')
    full_URL = '%s?item=%s' % (URL, item)
    return full_URL

Notez qu'il est possible de réécrire HTTPS_Response() en tant que décorateur . L'avantage serait de ne pas avoir à parcourir tout votre code et à remplacer HttpResponseRedirect(). L'inconvénient - il faudrait mettre le décorateur devant HttpResponseRedirect(), qui est dans Django à Django.http.__init__.py. Je ne voulais pas modifier Le code de Django, mais cela dépend de vous - c'est certainement une option.

20
John C

si vous collez l'intégralité de votre site derrière https, vous n'avez pas à vous en préoccuper à la fin Django. (en supposant que vous n'avez pas besoin de protéger vos données entre nginx et Django, seulement entre les utilisateurs et votre serveur)

5
second