web-dev-qa-db-fra.com

Vous essayez de faire en sorte qu'un site basé sur Django utilise uniquement HTTPS, vous ne savez pas s'il est sécurisé?

L'EFF recommande en utilisant HTTPS partout sur votre site, et je suis sûr que ce site serait d'accord. Quand j'ai posé une question sur l'utilisation de Django pour implémenter HTTPS sur ma page de connexion, c'était certainement la réponse que j'ai reçue :)

C'est ce que j'essaie de faire. J'ai une configuration Django/nginx que j'essaie de configurer pour HTTPS uniquement - cela fonctionne en quelque sorte, mais il y a des problèmes. Plus important encore, je suis sûr que c'est vraiment sécurisé , malgré le fait de voir https préfixe.

J'ai configuré nginx pour rediriger toutes les pages http vers https, et cette partie fonctionne. Cependant ... Disons que j'ai une page, https://mysite.com/search/, avec un formulaire/bouton de recherche dessus. Je clique sur le bouton, Django traite le formulaire et fait une redirection vers une page de résultats , qui est http://mysite.com/search/results?term="foo".

Cette URL est envoyée au navigateur, qui la renvoie au serveur nginx, qui effectue une redirection permanente vers un préfixe https- version de la page. (Au moins, je pense que c'est ce qui se passe - certainement IE me prévient que je vais sur une page non sécurisée , puis de retour à une page sécurisée :)

Mais est-ce vraiment sûr? Ou, au moins autant de sécurité qu'un site HTTPS standard aurait? Est-ce le fait que Django transmet une URL de préfixe http, quelqu'un compromettant la sécurité? Oui, pour autant que je sache, seules les pages qui ont un préfixe https reçoivent une réponse, mais ce n'est pas le cas je me sens juste :) La sécurité est géniale, comme ce site peut en témoigner, et je crains qu'il manque quelque chose.

63
John C

Sécurisez vos cookies

Dans settings.py mettez les lignes

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

et les cookies ne seront envoyés que via des connexions HTTPS. De plus, vous voulez probablement aussi SESSION_EXPIRE_AT_BROWSER_CLOSE=True. Notez que si vous utilisez des versions plus anciennes de Django (moins de 1,4), il n'y a pas de paramètre pour les cookies CSRF sécurisés. Comme solution rapide, vous pouvez simplement faire en sorte que les cookies CSRF soient sécurisés lorsque le le cookie de session est sécurisé (SESSION_COOKIE_SECURE=True), en modifiant Django/middleware/csrf.py:

class CsrfViewMiddleware(object):
   ...
   def process_response(self, request, response):
       ...
       response.set_cookie(settings.CSRF_COOKIE_NAME,
            request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
            domain=settings.CSRF_COOKIE_DOMAIN,
            secure=settings.SESSION_COOKIE_SECURE or None)

Diriger les requêtes HTTP vers HTTPS dans le serveur Web

Ensuite, vous voulez une règle de réécriture qui redirige les demandes http vers https, par exemple, dans nginx

server {
   listen 80;
   rewrite ^(.*) https://$Host$1 permanent;
}

La fonction reverse et les balises de modèle d'url de Django ne renvoient que des liens relatifs; donc si vous êtes sur une page https, vos liens vous garderont sur le site https.

Activez la variable d'environnement HTTPS du système d'exploitation

Enfin, (et ma réponse d'origine excluait cela), vous devez activer la variable d'environnement du système d'exploitation HTTPS à 'on' so Django ajoutera https aux liens entièrement générés (par exemple, comme avec HttpRedirectRequests). Si vous utilisez mod_wsgi, vous pouvez ajouter la ligne:

os.environ['HTTPS'] = "on"

à votre script wsgi . Si vous utilisez uwsgi, vous pouvez ajouter une variable d'environnement à l'aide du commutateur de ligne de commande --env HTTPS=on ou en ajoutant la ligne env = HTTPS=on à votre uwsgi .ini fichier. En dernier recours, si rien d'autre ne fonctionne, vous pouvez modifier votre fichier de paramètres pour que les lignes import os et os.environ['HTTPS'] = "on", qui devrait également fonctionner.

Si vous utilisez wsgi, vous pouvez également définir la variable d'environnement wsgi.url_scheme à 'https' en l'ajoutant à votre settings.py:

os.environ['wsgi.url_scheme'] = 'https'

Les conseils wsgi gracieuseté de commentaire de Vijayendra Bapte .

Vous pouvez voir la nécessité de cette variable d'environnement en lisant Django/http/__init__.py:

def build_absolute_uri(self, location=None):
    """
    Builds an absolute URI from the location and the variables available in
    this request. If no location is specified, the absolute URI is built on
    ``request.get_full_path()``.
    """
    if not location:
        location = self.get_full_path()
    if not absolute_http_url_re.match(location):
        current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
                                     self.get_Host(), self.path)
        location = urljoin(current_uri, location)
    return iri_to_uri(location)

def is_secure(self):
    return os.environ.get("HTTPS") == "on"

Autres choses sur le serveur Web:

Prenez les conseils de ce type et activez les en-têtes HSTS dans votre serveur Web en ajoutant une ligne à nginx:

add_header Strict-Transport-Security max-age=31536000;

Cela indique à votre navigateur Web que votre site Web utilisera uniquement HTTPS au cours des 10 prochaines années. S'il y a une attaque Man-in-the-middle lors d'une prochaine visite à partir du même navigateur (par exemple, vous vous connectez à un routeur malveillant dans un café qui vous redirige vers une version HTTP de la page), votre navigateur se souviendra il est censé être HTTPS uniquement et vous empêcher de renoncer par inadvertance à vos informations. Mais attention, vous ne pouvez pas changer d'avis et décider plus tard qu'une partie de votre domaine sera servie via HTTP (jusqu'à ce que les 10 ans se soient écoulés depuis la suppression de cette ligne). Alors planifiez à l'avance; Par exemple, si vous pensez que votre application pourrait bientôt gagner en popularité et que vous devrez utiliser un gros CDN qui ne gère pas bien HTTPS à un prix abordable, vous pouvez avoir un problème.

Assurez-vous également de désactiver les protocoles faibles. Soumettez votre domaine à un Test SSL pour vérifier les problèmes potentiels (clé trop courte, n'utilisez pas TLSv1.2, utilisez des protocoles cassés, etc.). Par exemple, dans nginx, j'utilise:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
67
dr jimbob

Rediriger de n'importe quel http: // vers la page https: // correspondante est la mauvaise approche. Configurez nginx pour rediriger le port 80 vers https: //votredomaine.ext/

server {
       listen 80;
       rewrite ^/? https://$Host/ permanent;
 }

ou similaire (consultez le prochain manuel nginx près de chez vous) et n'exécutez pas du tout votre application sur le port 80 (http). Ainsi, d'autres demandes sur le port 80 se résolvent en 404 ou similaire (personnalisez-le, en disant que votre application est désormais sécurisée et s'exécute uniquement sur https avec un lien pointant vers https: //votredomaine.ext/ ) . Exécutez ensuite votre application uniquement sur le port d'écoute 443 (https). L'utilisation de chemins relatifs dans votre code est désormais sécurisée, car ils se résolvent tous au chemin https: // complet et vous évitez le rebond de http vers https!

3
esskar

vous devez également envoyer un HSTS-Header de nginx, indiquant aux clients (navigateurs) qu'ils doivent utiliser HTTPS uniquement

add_header Strict-Transport-Security max-age=31536000;
3

Une configuration courante vous fera transférer du trafic https depuis votre serveur Web (c'est-à-dire Nginx) vers un serveur http local exécutant l'application Django.

Dans ce cas, il sera plus facile d'utiliser le SECURE_PROXY_SSL_HEADER paramètre (disponible depuis Django 1.4.)

https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER

3
Sebastian

Je pense que ce que vous recherchez est un middleware Django qui réécrira http en https. Quelque chose de similaire à ce qui est traité dans this question sur SO , où une réponse pointe vers ce middleware . Vous devrez probablement écrire votre propre middleware, mais il devrait être simple. (Une question bien ciblée sur SO vous orientera dans la bonne direction si vous avez besoin d'aide pour commencer.)

2
bstpierre

Dans la plupart des cas, vous pouvez définir Apache ou quelque chose pour rediriger vers https, comme décrit dans la réponse acceptée. Et si vous le pouvez, ce serait mieux pour les performances et pour les fichiers servis en dehors de Django.

Mais si vous ne pouvez pas, ou si vous voulez faire du débogage, je voudrais souligner que Django récemment (1.8) a introduit un SecurityMiddleware qui a https-redirects comme l'un de ses plusieurs fonctions.

Plus d'informations sont disponibles dans la documentation . Fondamentalement, ajoutez Django.middleware.security.SecurityMiddleware Et mettre SECURE_SSL_REDIRECT = True.

(L'en-tête mentionné par la réponse acceptée peut également être défini par ce middleware.)

2
Mark

Vous devez configurer Django pour générer soit

  1. https://domain/path liens avec le https: schéma,
  2. //domain/path des liens sans schéma (le navigateur interprétera ceux-ci comme ayant le même schéma que la page à laquelle il est actuellement ouvert), ou
  3. /path liens sans schéma ni domaine (le navigateur interprétera ceux-ci comme ayant le même schéma et le même domaine que la page vers laquelle il pointe actuellement).
1
yfeldblum