Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
433 views
in Technique[技术] by (71.8m points)

python - How to block SSL protocols in favor of TLS?

How can I block SSL protocols in PyOpenSSL in favour of TLS? I'm using CentOS 7 and have these versions:

pyOpenSSL-0.13.1-3.el7.x86_64
openssl-1.0.1e-34.el7_0.7.x86_64

In my config file (this if for a CherryPy app) I have:

'server.ssl_module': 'pyopenssl',
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is really good question for CherryPy today. This month we started discussing SSL issues and overall maintainability of CherryPy's wrappers over py2.6+ ssl and pyOpenSSL in CherryPy user group. I'm planning a topic about SSL issues there, so you can subscribe for the group to get more details later.

For now, here's what is possible. I had Debian Wheezy, Python 2.7.3-4+deb7u1, OpenSSL 1.0.1e-2+deb7u16. I've installed CherryPy from the repo (3.6 has broken SSL), and pyOpenSSL 0.14. I tried to override both CherryPy SSL adapters to gain some points in Qualys SSL labs test. It is very helpful and I strongly suggest you to test your deployment with it (whatever is your frontend, CherryPy or not).

As a result, ssl-based adapter still has vulnerabilities which I don't see the way to workaround in py2 < 2.7.9 (massive SSL update) and py3 < 3.3. Because CherryPy ssl adapter was written long before these changes, it needs a rewrite to support both old and new ways (mostly SSL Contexts). On the other hand with subclassed pyOpenSSL adapted it's mostly fine, except for:

Here's the code.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import sys
import ssl

import cherrypy
from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter
from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter

from cherrypy import wsgiserver
if sys.version_info < (3, 0):
  from cherrypy.wsgiserver.wsgiserver2 import ssl_adapters  
else:
  from cherrypy.wsgiserver.wsgiserver3 import ssl_adapters

try:
  from OpenSSL import SSL
except ImportError:
  pass


ciphers = (
  'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
  'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
  '!eNULL:!MD5:!DSS:!RC4:!SSLv2'
)

bundle = os.path.join(os.path.dirname(cherrypy.__file__), 'test', 'test.pem')

config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8443,
    'server.thread_pool' : 8,

    'server.ssl_module'      : 'custom-pyopenssl',
    'server.ssl_certificate' : bundle,
    'server.ssl_private_key' : bundle,
  }
}


class BuiltinSsl(BuiltinSSLAdapter):
  '''Vulnerable, on py2 < 2.7.9, py3 < 3.3:
    * POODLE (SSLv3), adding ``!SSLv3`` to cipher list makes it very incompatible
    * can't disable TLS compression (CRIME)
    * supports Secure Client-Initiated Renegotiation (DOS)
    * no Forward Secrecy
  Also session caching doesn't work. Some tweaks are posslbe, but don't really 
  change much. For example, it's possible to use ssl.PROTOCOL_TLSv1 instead of 
  ssl.PROTOCOL_SSLv23 with little worse compatiblity.
  '''

  def wrap(self, sock):
    """Wrap and return the given socket, plus WSGI environ entries."""
    try:
      s = ssl.wrap_socket(
        sock, 
        ciphers = ciphers, # the override is for this line
        do_handshake_on_connect = True,
        server_side = True, 
        certfile = self.certificate,
        keyfile = self.private_key,
        ssl_version = ssl.PROTOCOL_SSLv23
      )
    except ssl.SSLError:
      e = sys.exc_info()[1]
      if e.errno == ssl.SSL_ERROR_EOF:
        # This is almost certainly due to the cherrypy engine
        # 'pinging' the socket to assert it's connectable;
        # the 'ping' isn't SSL.
        return None, {}
      elif e.errno == ssl.SSL_ERROR_SSL:
        if e.args[1].endswith('http request'):
          # The client is speaking HTTP to an HTTPS server.
          raise wsgiserver.NoSSLError
        elif e.args[1].endswith('unknown protocol'):
          # The client is speaking some non-HTTP protocol.
          # Drop the conn.
          return None, {}
      raise

    return s, self.get_environ(s)

ssl_adapters['custom-ssl'] = BuiltinSsl


class Pyopenssl(pyOpenSSLAdapter):
  '''Mostly fine, except:
    * Secure Client-Initiated Renegotiation
    * no Forward Secrecy, SSL.OP_SINGLE_DH_USE could have helped but it didn't
  '''

  def get_context(self):
    """Return an SSL.Context from self attributes."""
    c = SSL.Context(SSL.SSLv23_METHOD)

    # override:
    c.set_options(SSL.OP_NO_COMPRESSION | SSL.OP_SINGLE_DH_USE | SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
    c.set_cipher_list(ciphers)

    c.use_privatekey_file(self.private_key)
    if self.certificate_chain:
        c.load_verify_locations(self.certificate_chain)
    c.use_certificate_file(self.certificate)
    return c

ssl_adapters['custom-pyopenssl'] = Pyopenssl


class App:

  @cherrypy.expose
  def index(self):
    return '<em>Is this secure?</em>'


if __name__ == '__main__':
  cherrypy.quickstart(App(), '/', config)

Update

Here's the article and discussion where future of CherryPy's SSL support should be decided.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...