How to block SSL protocols in favor of TLS?

前端 未结 2 1880
广开言路
广开言路 2020-12-04 00:15

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

pyOpenSSL-0.         


        
2条回答
  •  生来不讨喜
    2020-12-04 00:32

    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:

    • Enabled Secure Client-Initiated Renegotiation. It may be OpenSSL-dependent.
    • no Forward Secrecy, SSL.OP_SINGLE_DH_USE could have helped but it didn't. May also depend on version of OpenSSL.

    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 'Is this secure?'
    
    
    if __name__ == '__main__':
      cherrypy.quickstart(App(), '/', config)
    

    Update

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

提交回复
热议问题