python - 更改 Python 使用的 TLS 版本
问题描述
我想连接其所有者告诉我需要连接 TLS 1.2 的服务。
问题是我的 Python 使用 TLS 1.3 我用这个命令检查了它python -c "import requests; print(requests.get('https://www.howsmyssl.com/a/check', verify=False).json()['tls_version'])"
是否可以将 TLS 降级到 1.2?
解决方案
出色地,
第一的
你应该知道 ssl 或者今天叫做 tls,是一个握手“互相同意”的过程,首先打开 TCP 套接字,然后通过这个套接字的通信将被隐藏加密或者你可以用wireshark 嗅探,因为一个“套接字的“包装”,这是使用 python 或其他语言时使用的常规方法,我的意思是只是一个中间类为你做加密,你甚至不小心,最后客户端和服务器之间使用的共享加密套件是以前的“同意”这个提到的握手过程。
第二
HTTP 只是 TCP 上的另一个应用层,但真正的测试将始终在传输层上,我将在下一行分享我自己的 tls/ssl 程序测试器以减轻理论缺陷。
你可以像python3.x.exe一样运行它_ _ _ _,但是您始终可以使用选项-tls ../location/other.crt ../location/other.key指定它们
local.crt和local.key的原因基本上是握手是交换密钥(local.xxx文件)和远程服务器文件的过程,所以在我的下一个测试器示例中,我总是自定义 ssl 上下文变量基于那些local.xxx文件,请注意在其上使用.load_cert_chain
无论如何,您可以使用这样的 openssl 命令非常轻松地生成它们openssl req -newkey rsa:2048 -new -x509 -nodes -days 3650 -keyout llave.key -out cert.crt -subj "/C=CO/ST=ANT/ L=Medellin/O=YourCompany/OU=YourArea/CN=YourDevice/emailAddress=joseveland@gmail.com"或类似https://www.ssl.com/online-csr-and-key-generator/的网站将它们保存在您的单独 .crt 和 .key 文件中
import os, platform, socket, ssl
if platform.system().lower() == 'linux': # Linux OS ..
windows = False
slash_principal = '/'
slash_secundario = '\\'
limpiar_pantalla = lambda :os.system('clear')
else: # Windows OS ..
windows = True
slash_principal = '\\'
slash_secundario = '/'
limpiar_pantalla = lambda :os.system('cls')
import argparse, errno
limpiar_pantalla()
filepath = os.path.dirname(os.path.abspath(__file__))
ssl_files_def = [filepath+slash_principal+'local.crt', filepath+slash_principal+'local.key']
parser = argparse.ArgumentParser( description="Probador de TLS")
parser.add_argument( '-ip', nargs=1, metavar='IP', default=['www.google.com'], type=str, help=' IP del socket TCP a probar') # 34.196.130.67 EPSA Pruebas
parser.add_argument( '-puerto', nargs=1, metavar='PUERTO', default=[443], type=int, help=' Puerto del socket TCP a probar')
parser.add_argument( '-version', nargs=1, metavar='TLS_VERSION', type=int, choices=[0,1,2,3], help=' Version TLS a probar si no se especifica se hara AUTO')
parser.add_argument( '-tls', nargs=2, metavar=('CRT', 'KEY'), default=ssl_files_def, type=str, help=' Ruta al archivo ".crt" ..y.. ruta al archivo ".key" locales, para ejecutar el handshake/intercambio TLS con alguien remoto')
parser_opts = parser.parse_args()
#print("Argumentos -->", parser_opts._get_kwargs())
#print()
# ---------------- Logica ---------------- #
sock_pair = ( parser_opts.ip[0], parser_opts.puerto[0] )
sock_tls_ver = ssl.PROTOCOL_TLS # Auto
if parser_opts.version: # Se ingreso en el parse? (!= None) ya que no es obligatoria
if parser_opts.version[0] == 0:
sock_tls_ver = ssl.PROTOCOL_TLSv1
elif parser_opts.version[0] == 1:
sock_tls_ver = ssl.PROTOCOL_TLSv1_1
elif parser_opts.version[0] == 2:
sock_tls_ver = ssl.PROTOCOL_TLSv1_2
#elif parser_opts.version[0] == 3:
# sock_tls_ver = ssl.PROTOCOL_TLSv1_3
sslCntx = ssl.SSLContext(sock_tls_ver) # https://docs.python.org/3/library/ssl.html#ssl.SSLContext
# Con las siguientes opciones se evitan suites SSL inseguras (Al final solo permitira >= TLSv1..)
sslCntx.options |= ssl.OP_NO_SSLv2
sslCntx.options |= ssl.OP_NO_SSLv3
if parser_opts.version: # Se ingreso en el parse? (!= None) ya que no es obligatoria
if parser_opts.version[0] >= 1:
sslCntx.options |= ssl.OP_NO_TLSv1 # Evite la version 0 de TLS al conectar/handshake (version >= v1.1)
if parser_opts.version[0] >= 2:
sslCntx.options |= ssl.OP_NO_TLSv1_1 # Evite la version 1 de TLS al conectar/handshake (version >= v1.2)
if parser_opts.version[0] >= 3:
sslCntx.options |= ssl.OP_NO_TLSv1_2 # Evite la version 2 de TLS al conectar/handshake (version >= v1.3)
sslCntx.load_cert_chain(*ssl_files_def) # Finalmente cargue mis llaves con las que hare el handshake.
print()
print('Versiones SSL (Obsoleto, demostrativo):', int(ssl.PROTOCOL_SSLv23)) # OBSOLETO HACE RATO.
print('Versiones TLS:', int(ssl.PROTOCOL_TLSv1), int(ssl.PROTOCOL_TLSv1_1), int(ssl.PROTOCOL_TLSv1_2))#, ssl.PROTOCOL_TLSv1_3)
print('Mi Contexto:', sslCntx.options, int(sslCntx.minimum_version), int(sslCntx.maximum_version), sslCntx.verify_flags, sslCntx.verify_mode, sslCntx.get_ca_certs())#, sslCntx.get_ciphers(), dir(sslCntx))
print('Remoto:', sock_pair)
print()
s = socket.socket()
s_tls = sslCntx.wrap_socket(s) # Wrap lo convierte en un socket que hara handshake TLS y la comunicacion sera encriptada por ello.
try:
print('Socket Ok:', s_tls.version(), s_tls.session, s_tls.shared_ciphers() )#, dir(s_tls))
print()
s_tls.connect(sock_pair)
print('Conexion Ok:', s_tls.version(), s_tls.session.timeout)
print( s_tls.shared_ciphers() )#, dir(s_tls.session), dir(s_tls))
print()
s_tls.close()
except socket.error as e:
print('Socket FALLO (', os.strerror(e.errno), ')')
except Exception as e:
print('Conexion FALLO:', type(e).__name__, e)
print()
注意:此程序还可用于测试您想要的任何套接字/页面,并指定 tls 版本作为选项-version 0、-version 1、-version 2、-version 3以强制使用特定的 TLS 版本,并可用于检测可降级(不安全的)服务器
第三
概念很清楚,如果您已经想通过应用程序层 python 模块测试 TLS,您可能希望在此所需模块上放置或指定先前代码的此自定义sslCntx(ssl 上下文)变量,以便它可以根据文档的不同而有所不同它。
我的意思是换这个...
s = socket.socket()
s_tls = sslCntx.wrap_socket(s) # Wrap lo convierte en un socket que hara handshake TLS y la comunicacion sera encriptada por ello.
try:
print('Socket Ok:', s_tls.session, s_tls.version(), s_tls.shared_ciphers(), s_tls )#, dir(s_tls))
print()
s_tls.connect(sock_pair)
print('Conexion Ok:', s_tls.session.timeout, s_tls.version(), s_tls.shared_ciphers(), s_tls )#, dir(s_tls.session), dir(s_tls))
print()
s_tls.close()
...具有所需模块的细节,例如在http模块上将是这样的参数“上下文”...
from http.client import HTTPSConnection
try:
conn = HTTPSConnection(*sock_pair, context=sslCntx)
print('HTTP Ok:', conn.host, conn.port)#, dir(conn))
print()
conn.request( 'GET', '/' )
ans = conn.getresponse()
print('GET', ans.reason, ':', ans.status)
print(ans.headers)#, dir(ans))
conn.close()
print()
并像python3.x.exe program.py -ip www.whatever.com -puerto 443 -version 2一样运行它,因为在您要求强制使用 TLSv1.2 的问题上
第四
另一方面,当不使用-version选项时,默认的 python 参数“sock_tls_ver = ssl.PROTOCOL_TLS”将为您完成这项工作,就像它应该尝试连接所有 tls 版本,所以如果您的远程服务器甚至只支持 TLSv1.2如果您的 python 使用 TLSv1.3 编译,它将使用 TLSv1.2 并按预期连接(降级到 TLS1.2 没有问题)。
您可以检查您是否支持打印布尔值的 TLS 版本...
import ssl
print(ssl.HAS_TLSv1)
print(ssl.HAS_TLSv1_1)
print(ssl.HAS_TLSv1_2)
print(ssl.HAS_TLSv1_3)
希望这有助于消除 SSL/TLS 只是在发送任何套接字有价值的数据(如 http 请求)之前的一个小过程(在客户端和服务器之间交换安全问题)。
顺便说一句,SSL 将名称更改为 TLS,并且多年来一直是可破解的,这就是我们有这么多版本的原因(所以互联网不像你想象的那么安全)
推荐阅读
- java - 两个线程上的进度条
- ffmpeg - 如何从 ffmpeg 获得 4 种不同的质量?
- c++ - 如何格式化此数据以发送到串行端口?
- c - 试图从用户那里读取一个句子并写入文本文件
- python - 并行化 groupby:将函数同时应用于 groupby 对象
- amazon-web-services - 如何在使用 aws cdk 创建自动缩放组时选择固定的 aws linux 映像机器?
- php - URI https://www.googleapis.com/oauth2/v4/token 的连接被拒绝
- unit-testing - 为什么 Spock 不记录由间谍对象的方法完成的调用?
- debugging - 在某些情况下,Xdebug 2.9.8 不会在 NetBeans 8.1 中的断点处中断
- java - 了解 Java 中的锁用法