【问题标题】:python ssl ssl.SSLError: [SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:590)python ssl ssl.SSLError: [SSL: UNSUPPORTED_PROTOCOL] 不支持的协议 (_ssl.c:590)
【发布时间】:2015-09-01 11:52:43
【问题描述】:

使用 python 2.7.10 或 2.7.9 时出现此错误,但适用于 python 2.7.6(尚未测试其他版本)

OpenSLL 版本:

openssl version -a
OpenSSL 1.0.1f 6 Jan 2014
built on: Thu Jun 11 15:30:15 UTC 2015
platform: debian-amd64
options:  bn(64,64) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)
compiler: cc -fPIC -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -DTERMIO -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -Wl,-Bsymbolic-functions -Wl,-z,relro -Wa,--noexecstack -Wall -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM -DWHIRLPOOL_ASM -DGHASH_ASM
OPENSSLDIR: "/usr/lib/ssl"

Python:

import ssl
print ssl.OPENSSL_VERSION_NUMBER
268439663L
print get_server_certificate(('someInternalIp', 443), ssl_version=ssl.PROTOCOL_SSLv23)

堆栈跟踪:

File "C:\Python27\lib\ssl.py", line 985, in get_server_certificate
  with closing(context.wrap_socket(sock)) as sslsock:
File "C:\Python27\lib\ssl.py", line 352, in wrap_socket
  _context=self)
File "C:\Python27\lib\ssl.py", line 579, in __init__
  self.do_handshake()
File "C:\Python27\lib\ssl.py", line 808, in do_handshake
  self._sslobj.do_handshake()
ssl.SSLError: [SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:590)

【问题讨论】:

    标签: python ssl


    【解决方案1】:

    如果您在基于 debian 的操作系统或 docker 映像中运行您的请求,您需要更改您的 SSL 配置。 Debian 目前默认使用 SSL v1.2 +。

    您可以根据需要手动编辑 openssl.cnf,但有一个更快的解决方案:

    sed -i 's/MinProtocol = TLSv1.2/MinProtocol = TLSv1.0/' /etc/ssl/openssl.cnf
    

    在我的情况下,对使用旧 SSL 的 URL 的请求现在会返回不错的 200 响应。

    【讨论】:

      【解决方案2】:

      显然 python 2.7.6 使用了这个密码套件:

      DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2
      

      现在它使用:

      # Disable weak or insecure ciphers by default
      # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
      # Enable a better set of ciphers by default
      # This list has been explicitly chosen to:
      #   * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE)
      #   * Prefer ECDHE over DHE for better performance
      #   * Prefer any AES-GCM over any AES-CBC for better performance and security
      #   * Then Use HIGH cipher suites as a fallback
      #   * Then Use 3DES as fallback which is secure but slow
      #   * Disable NULL authentication, NULL encryption, and MD5 MACs for security
      #     reasons
      _DEFAULT_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'
      )
      

      我必须构建以下代码,以在使用旧密码套件时通知我。我可以这样做,因为我有兴趣检索证书

      from socket import socket
      from ssl import SSLContext
      from ssl import PROTOCOL_SSLv23
      from ssl import DER_cert_to_PEM_cert
      
      WEAK_CTX = SSLContext(PROTOCOL_SSLv23)
      WEAK_CTX.set_ciphers('ALL:!aNULL:!eNULL')
      
      NORMAL_CTX = SSLContext(PROTOCOL_SSLv23)
      NORMAL_CTX.set_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'
      )
      
      def getCertificate(addr):
          sock = socket()
          sock.connect(addr)
          isWeakCipher = False
          try:
              sslobj = NORMAL_CTX._wrap_socket(sock._sock, server_side=False)
              sslobj.do_handshake()
          except Exception as ex:
              if hasattr(ex, 'reason') and ex.reason == 'SSLV3_ALERT_HANDSHAKE_FAILURE':
                  sock.close()
                  sock = socket()
                  sock.connect(addr)
                  sslobj = WEAK_CTX._wrap_socket(sock._sock, server_side=False)
                  sslobj.do_handshake()
                  isWeakCipher = True
              else:
                  raise
          cipher = sslobj.cipher()
      
          cert = sslobj.peer_certificate(True)
          sock.close()
          return isWeakCipher, cipher, cert 
      

      【讨论】:

      • 有人可以进一步解释这个答案吗?当我收到该错误时该怎么办?
      • 在大多数情况下,您想要更改目标机器上的设置。很可能它使用的是较旧的协议版本。上面的代码试图“不顾一切地进入”,这在正常生产情况下不应该做。尝试将您的目标机器设置为使用 TLS。最好是 1.2,因为这是目前最新的。
      • 拯救了我的一天。我可以确认必须设置密码才能使用 SSLv3 连接。
      • 就我而言,我不得不使用ssl.PROTOCOL_TLSv1 来强制使用 TLS v1.0。我不确定发生了什么,因为PROTOCOL_TLSPROTOCOL_SSLv23 的同义词)的描述说它应该自动选择一个兼容的选项:docs.python.org/3/library/ssl.html#ssl.PROTOCOL_TLS
      【解决方案3】:

      可能您的服务器暴露了旧的、不安全的 TLS 协议版本,现代 OpenSSL 被配置为不允许。

      https://stackoverflow.com/a/53065682/6214034 可能会有所帮助。

      【讨论】:

        【解决方案4】:

        这就是我在 Ubuntu 20.04 上使用 python 3.8 解决它的方法。

        import ssl
        import socket
        
        def get_ssl_data(self, host, port=443):
            context = ssl.create_default_context()
            context.set_ciphers('ALL:@SECLEVEL=1') # this magic line allows us to connect with anything under the sun!!! It took me 3 hours to find it.
        
            with socket.socket(socket.AF_INET) as sock:
                with context.wrap_socket( sock, server_hostname=host ) as conn:
                    conn.settimeout(3.0)
                    conn.connect((host, port))
                    ssl_info = conn.getpeercert()
                    # print(json.dumps(ssl_info, indent=2, sort_keys=True))
                    return ssl_info
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-29
          • 1970-01-01
          • 2020-01-24
          • 1970-01-01
          • 2017-11-22
          • 2021-11-03
          • 2015-12-21
          • 2019-05-10
          相关资源
          最近更新 更多