我正在使用Python和请求抓取一些内部页面。我已经关闭了SSL验证和警告。
requests.packages.urllib3.disable_warnings()
page = requests.get(url, verify=False)
在某些服务器上,我收到一个SSL错误,我无法通过。
Traceback (most recent call last):
File "scraper.py", line 6, in <module>
page = requests.get(url, verify=False)
File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/api.py", line 71, in get
return request('get', url, params=params, **kwargs)
File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/api.py", line 57, in request
return session.request(method=method, url=url, **kwargs)
File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/sessions.py", line 475, in request
resp = self.send(prep, **send_kwargs)
File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/sessions.py", line 585, in send
r = adapter.send(request, **kwargs)
File "/cygdrive/c/Users/jfeocco/VirtualEnv/scraping/lib/python3.4/site-packages/requests/adapters.py", line 477, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: [SSL: SSL_NEGATIVE_LENGTH] dh key too small (_ssl.c:600)
这种情况发生在Cygwin的内部/外部,Windows和OSX中。我的研究暗示了服务器上过时的OpenSSL。我正在寻找一个理想的修复客户端。
编辑:我能够通过使用密码集来解决这个问题
import requests
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL'
try:
requests.packages.urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST += 'HIGH:!DH:!aNULL'
except AttributeError:
# no pyopenssl support used / needed / available
pass
page = requests.get(url, verify=False)
禁用警告或证书验证无济于事。根本问题是服务器使用的弱DH密钥,可能在Logjam Attack中被滥用。
要解决此问题,您需要选择一个不使用Diffie Hellman密钥交换的密码,因此不受弱DH密钥的影响。并且此密码必须由服务器支持。不知道服务器支持什么,但您可以尝试使用密码AES128-SHA
或密码集HIGH:!DH:!aNULL
使用您自己的密码集请求是棘手的。有关示例,请参阅Why does Python requests ignore the verify parameter?。
这不是一个额外的答案只是尝试将问题的解决方案代码与额外的信息结合起来所以其他人可以直接复制它而无需额外的尝试
它不仅是服务器端的DH Key问题,而且许多不同的库在python模块中也不匹配。
下面的代码段用于忽略这些安全问题,因为它可能无法在服务器端解决。例如,如果它是内部旧服务器,则没有人想要更新它。
除了'HIGH:!DH:!aNULL'
被黑的字符串外,还可以导入urllib3模块以禁用警告
import requests
import urllib3
requests.packages.urllib3.disable_warnings()
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL'
try:
requests.packages.urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST += 'HIGH:!DH:!aNULL'
except AttributeError:
# no pyopenssl support used / needed / available
pass
page = requests.get(url, verify=False)
我会在这里打包我的解决方案。我不得不修改python SSL库,这可能是因为我在docker容器中运行我的代码,但这可能是你不想做的事情。
check_supported_ciphers.是
#!/usr/bin/env bash
# OpenSSL requires the port number.
SERVER=$1
DELAY=1
ciphers=$(openssl ciphers 'ALL:eNULL' | sed -e 's/:/ /g')
echo Obtaining cipher list from $(openssl version).
for cipher in ${ciphers[@]}
do
echo -n Testing $cipher...
result=$(echo -n | openssl s_client -cipher "$cipher" -connect $SERVER 2>&1)
if [[ "$result" =~ ":error:" ]] ; then
error=$(echo -n $result | cut -d':' -f6)
echo NO \($error\)
else
if [[ "$result" =~ "Cipher is ${cipher}" || "$result" =~ "Cipher :" ]] ; then
echo YES
else
echo UNKNOWN RESPONSE
echo $result
fi
fi
sleep $DELAY
done
授予权限:
chmod +x check_supported_ciphers.sh
并执行它:
./check_supported_ciphers.sh myremoteserver.example.com | grep OK
几秒钟后,您将看到类似于的输出:
Testing AES128-SHA...YES (AES128-SHA_set_cipher_list)
因此将使用“AES128-SHA”作为SSL密码。
并替换:
_DEFAULT_CIPHERS = (
'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'
)
通过:
_DEFAULT_CIPHERS = (
'AES128-SHA'
)