python3 SSLCertVerificationError 研究
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))': /simple/frida-tools/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))': /simple/frida-tools/ WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))': /simple/frida-tools/ WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))': /simple/frida-tools/ WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))': /simple/frida-tools/ Could not fetch URL https://pypi.org/simple/frida-tools/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/frida-tools/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1000)'))) - skippin
python3 ssl验证出错,因为开启了https代理, (启动了charles工具)
我知道怎么解决这个问题, 就是关闭代理工具。但是我想探探它怎么验证的, 因为浏览器可以
1. 写了一个nodejs代码做测试, 这个是http, 直接请求, charles并没有抓到包
const axios = require('axios'); async function requestBaidu() { try { const response = await axios.get('http://www.baidu.com'); console.log('Status Code:', response.status); console.log('Response Headers:', response.headers); console.log('Response Data:', response.data.substring(0, 100)); // 输出前100个字符 } catch (error) { console.error('Error requesting Baidu:', error); } } requestBaidu();
我发现了在命令行设置: export HTTP_PROXY="http://127.0.0.1:8888", 再次运行就可以抓到了。
2. 继续探路, 用python试试
import requests # 发起请求 response = requests.get('http://www.baidu.com') # 打印响应内容 print(response.text)
执行结果非常好,Charles能抓包到,
通过1和2,说明了python3默认是走代理的!
3. 将1和2的代码,全部改成https
在设置了HTTP(S)_PROXY的终端分别执行js和python脚本: js可以正常访问,能抓包。 python不能正常访问,能抓包
在没有设置HTTP(S)_PROXY的终端分别执行js和python脚本: js可以正常访问,没抓到包。 python不能正常访问,能抓包
通过这些测试发现了一点东西,python默认走系统代码,并且验证比nodejs严格。 nodejs默认不走代理,如果设置了环境变量代理,相当于和浏览器一样了。
4. 这个结论比较有趣, 不过至少可以证明任何程序,走不走代理是app应用自己说了算! 下面的python代码,证明了这个结论:
import http.client # 创建连接 connection = http.client.HTTPConnection('baidu.com') # 发送 GET 请求 connection.request('GET', '/') # 你可以根据需要更改请求路径 # 获取响应 response = connection.getresponse() # 输出状态和内容 print(f'Status: {response.status}, Reason: {response.reason}') print(response.read().decode()) # 关闭连接 connection.close()
使用最原始的请求, 不管有没有设置http_proxy, 它都不会走代理。对它进行改造
我通过断点跟踪,发现requests库默认环境变量和系统代理。 优先获取代理环境变量,然后在获取系统代理。
nodejs axios 仅仅只是获取代理环境变量.
5. 那么现在就只有一个问题: 都走代理的情况下, 为啥nodejs可以,python不行
开启逆向之旅:
错误信息的关键: self-signed certificate
hopper载入:
/usr/local/opt/python@3.12/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_ssl.cpython-312-darwin.so
没有搜索到,那么查看这个so的依赖:发现了
/usr/local/opt/openssl@3/lib/libssl.3.dylib
hopper 载入它:
还是没有, 我确定它是openssl报出来的, 那么就全局查找吧, 发现了:
居然在libcrypto.3.dylib里面, 简直不敢相信, 好吧,继续跟踪它:
继续跟踪,看下它的触发条件,发现没有任何地方引用它... 那么就在网上拉去openssl源码吧, 它是开源的:
https://openssl-library.org/source/index.html
错误编号:X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
python里有一个验证选项,如果为true,那么
python3 也就是会默认使用VERIFY_PEER 模式,这个模式就是会让openssl库进行证书检查! 看了nodejs相关源码, https默认的模式是VERIFY_NONE, 所以不会进行任何报错!
解决方案,证书验证的时候
1. requests.get("httpsUrl", verify=False)
2. 控制台 export REQUESTS_CA_BUNDLE=/path/charles-ssl-proxying-certificate.pem
或者export CURL_CA_BUNDLE=/path/charles-ssl-proxying-certificate.pem