Gunicorn: Le paramètre de chiffrement SSL par défaut désactive les chiffrements les plus sécurisés

Créé le 23 janv. 2019  ·  3Commentaires  ·  Source: benoitc/gunicorn

La valeur par défaut de l'option --ciphers est TLSv1 .

Cette valeur est une mauvaise valeur par défaut, car elle désactive activement les nouveaux chiffrements forts qui ne sont disponibles qu'avec TLSv1.2. En particulier, il n'y a pas d'intersection entre l'ensemble de chiffrements configuré par cette valeur par défaut et les chiffrements notés A+ par l'OWASP :

>>> import ssl
>>> ctx = ssl.SSLContext()
>>> ctx.set_ciphers('TLSv1')
>>> tlsv1 = {c['name'] for c in ctx.get_ciphers()}
>>> ctx.set_ciphers('DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256')  # From OWASP
>>> aplus = {c['name'] for c in ctx.get_ciphers()}
>>> aplus & tlsv1
set()

Cela peut entraîner des erreurs OpenSSL NO_SHARED_CIPHER pour les clients configurés avec des paramètres de haute sécurité.

Une solution serait d'ajouter simplement des chiffrements TLSv1.2 à l'ensemble, en remplaçant la valeur par défaut par la chaîne 'TLSv1:TLSv1.2' . Cela ajouterait de la compatibilité pour les clients configurés avec des paramètres de sécurité stricts, sans casser les utilisateurs existants de gunicorn. Cependant, cela laisserait des chiffres faibles dans l'ensemble.

Une option plus sûre serait de choisir l'une des chaînes OWASP par défaut, en fonction du niveau de compatibilité décrit. Par exemple, la chaîne de chiffrement OWASP C ou C- préserverait une large compatibilité tout en excluant les chiffrements faibles connus.

Dans les deux cas, la documentation pourrait être mise à jour pour recommander de remplacer la valeur par défaut par une valeur par défaut plus forte basée sur l'utilisation attendue, y compris un lien vers la page OWASP comme référence.

Feedback Requested FeaturSSL

Commentaire le plus utile

De plus, Python a sa propre liste de chiffrement par défaut : il ne revient jamais à la valeur par défaut d'OpenSSL (qui est apparemment DEFAULT:!aNULL:!eNULL ). Il est accessible en tant que ssl._DEFAULT_CIPHERS bien que ce soit interne/non documenté, donc le moyen d'accéder aux valeurs par défaut est de ne jamais appeler set_ciphers() sur un SSLContext.

Pour Python 3.6 _DEFAULT_CIPHERS est

'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES'

En Python 3.7 c'est

'DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK'

avec un commentaire disant "DEFAULT : liste de chiffrement par défaut d'OpenSSL. Depuis la 1.0.2, la liste est dans un ordre raisonnable."

Ces réglages semblent assez serrés, et dans le même esprit que l'OWASP.

Il y a aussi un _RESTRICTED_SERVER_CIPHERS qui est destiné à être utilisé dans les serveurs (donc parfait pour gunicorn) et est plus serré que _DEFAULT_CIPHERS dans certaines versions de Python, mais qui est à la fois interne et n'est utilisé nulle part - mais a été maintenu depuis au moins 3.4.

L'option 3 est donc par défaut la suite de chiffrement par défaut de Python.

La seule raison de ne pas le faire est si nous pensons pouvoir envoyer une liste aujourd'hui plus sécurisée que celle des anciennes versions de Python, ou si nous voulons opter pour l'un des niveaux OWASP encore plus stricts. Honnêtement, c'est assez marginal.

Tous les 3 commentaires

Merci d'avoir signalé ce problème. Nous en sommes parfaitement conscients. Gunicorn n'a pas eu de changements significatifs dans sa prise en charge SSL depuis un certain temps. Voir #1933 et les problèmes liés à cette discussion.

En attendant, s'il existe une valeur par défaut plus raisonnable maintenant que Gunicorn ne prend en charge que Python 3.4+, veuillez soumettre une pull request et je serais heureux de la fusionner. Nous envisageons également de pousser cette version minimale plus haut, donc s'il existe une version minimale de Python plus élevée qui permettrait une valeur par défaut encore plus sécurisée, veuillez publier ces informations ici pour aider à prendre cette décision avant la prochaine version.

Les chiffrements disponibles (et l'interprétation de l'argument de SSLContext.set_ciphers() ) ne sont pas affectés par la version de Python, mais par la version d'OpenSSL/LibreSSL contre laquelle Python se base. Sous Linux, ce serait généralement la version fournie par la distribution.

Si votre question est "pouvons-nous définir une valeur par défaut plus sécurisée qui fonctionne toujours dans Python 3.4+", alors la réponse stricte est "non" car cela dépend de la façon dont vous avez compilé Python. Il peut être possible de compiler un Python récent contre un très ancien OpenSSL qui manque de chiffrements forts, donc exiger des chiffrements forts dans gunicorn pourrait signifier que SSL ne fonctionne pas du tout.

Cependant, je pense que nous sommes assez en sécurité. Python 3.4 a été publié en 2014, tandis qu'OpenSSL 1.0.1a, qui a ajouté TLSv1.2, a été publié en avril 2012. Ainsi, les versions de distribution de Python auront probablement des chiffrements puissants. L'autre chose est que chaque version de Python nécessite une certaine version d'OpenSSL (par exemple, Python 3.7 nécessite OpenSSL 1.0.2, je comprends), mais je ne sais pas exactement quelles versions de Python nécessitent quoi.

Dans l'ensemble, je devrais être guidé par l'OWASP. Ils disent que C- est "Héritage, compatibilité la plus large avec les vrais vieux navigateurs et bibliothèques héritées" - avec l'avantage supplémentaire qu'ils peuvent être cités comme source.

De plus, Python a sa propre liste de chiffrement par défaut : il ne revient jamais à la valeur par défaut d'OpenSSL (qui est apparemment DEFAULT:!aNULL:!eNULL ). Il est accessible en tant que ssl._DEFAULT_CIPHERS bien que ce soit interne/non documenté, donc le moyen d'accéder aux valeurs par défaut est de ne jamais appeler set_ciphers() sur un SSLContext.

Pour Python 3.6 _DEFAULT_CIPHERS est

'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES'

En Python 3.7 c'est

'DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK'

avec un commentaire disant "DEFAULT : liste de chiffrement par défaut d'OpenSSL. Depuis la 1.0.2, la liste est dans un ordre raisonnable."

Ces réglages semblent assez serrés, et dans le même esprit que l'OWASP.

Il y a aussi un _RESTRICTED_SERVER_CIPHERS qui est destiné à être utilisé dans les serveurs (donc parfait pour gunicorn) et est plus serré que _DEFAULT_CIPHERS dans certaines versions de Python, mais qui est à la fois interne et n'est utilisé nulle part - mais a été maintenu depuis au moins 3.4.

L'option 3 est donc par défaut la suite de chiffrement par défaut de Python.

La seule raison de ne pas le faire est si nous pensons pouvoir envoyer une liste aujourd'hui plus sécurisée que celle des anciennes versions de Python, ou si nous voulons opter pour l'un des niveaux OWASP encore plus stricts. Honnêtement, c'est assez marginal.

Cette page vous a été utile?
0 / 5 - 0 notes