How to fix CERTIFICATE_VERIFY_FAILED error in websocket-client Python module?
Asked Answered
T

5

7

Code:

import websocket
ws = websocket.WebSocket()
ws.connect('wss://stream2.binance.com:9443/ws/!miniTicker@arr@3000ms')
record  = ws.recv()
print(record)

I was trying to get realtime data from Binance Websocket API. while trying to fetch data with this sample url

wss://stream.binance.com:9443/ws/bnbbtc@depth

I am getting this error which says the SSL Verification is failed.

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)

Traceback : pastebin.com/RiHn025Z

What I've already tried:

So I found this question on SO How to create Python secure websocket client request? and followed the steps with this code

ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
      ws.connect("wss://stream2.binance.com:9443/ws/!miniTicker@arr@3000ms")

But then a NameError occured:

NameError: name 'ssl' is not defined

I tried to add an Exception (which is ridiculous but still...) which resulted in SyntaxError.

Other Scopes

I tried with different websocket API which uses wss:// but worked just fine in the first code itself.

wss://ws.blockchain.info/inv
{"op":"ping"}

Conditions:

I tried an Echo Test on websockets.org and the wss url is fully functional.

Any help will be appreciated. there are other modules available specifically for binance but I'd like to have the raw data so I am using this api.

Thanks for reading my Question.

GitHub URL for websocket-client : https://github.com/websocket-client/websocket-client

Tomblin answered 5/3, 2018 at 13:29 Comment(4)
Your sample code raises a different exception when I try it, TypeError: __init__() takes exactly 4 arguments (1 given) for the WebSocket initializer. What version of websocket are you using? Does your example actually run for you?Sabinasabine
I am getting a really dirty error pastebin.com/RiHn025Z You'll find the SSL Error in the last line of code.Tomblin
No one on stackoverflow will mind if you include that traceback in your question. In fact, it's preferred. But you didn't answer the question about what version of websocket you're using.Sabinasabine
I'll keep that in mind. the error was lengthy so I just included the last part. I am using websocket-client 0.47.0Tomblin
B
9

On Mac OS X, the problem is resolved by clicking on the "Install Certificates.command" file located in the Python directory of the Applications folder.

To run the command, open a new Finder window. Click on "Applications". Then click on the directory where Python is installed. For example, "Python 3.7". Finally, kick on the "Install Certificates.command file.

All of this can be accomplished by executing the following command in the Terminal application:

open "/Applications/Python 3.7/Install Certificates.command"

NOTE: You need to be logged into the account that downloaded and installed Python 3.7.

Binion answered 20/4, 2020 at 21:23 Comment(1)
Indeed this problem seems related only to the websocket-client library, combined with OSX and the Binance Websocket. This solution did fix everything for me and it avoids disabling the ssl certificate verification. It actually updates the certifi package to its latest version with: pip install --upgrade certifiWhiny
Q
5

The sslopt={"cert_reqs": ssl.CERT_NONE} way is correct. When you get a NameError: NameError: name 'ssl' is not defined you need to import ssl and it should work. Worked for my SSL-related problem.

Quijano answered 14/4, 2018 at 18:49 Comment(0)
S
4

It appears that websocket-client ships its own root certificates bundle (bad idea) and it ships a bundle that does not include the particular CA certificate for the CA which signed stream2.binance.com's certificate (oops).

You can fix this by pointing websocket-client at a better bundle. For example, on Ubuntu, I have a good bundle provided by the OS at /etc/ssl/certs/ca-certificates.pem. Therefore:

WEBSOCKET_CLIENT_CA_BUNDLE=/etc/ssl/certs/ca-certificates.pem python wsexample.py

Doing this, I get some data dumped by your example program, presumably the data you're after.

Better would be a way to tell websocket-client to use the OS-supplied default root certificate bundle. However, I don't see an easy way to do this with websocket-client. You may want to take a look at Autobahn as a more featureful and reliable alternative.

Sabinasabine answered 5/3, 2018 at 14:1 Comment(2)
I found many issues in this library on GitHub. I guess, I'll need to see it through Autobahn. I hope it's a flexible module which can be used with different Websockets. Thanks for your effort.Tomblin
@superhead are you running autobahn inside celery tasks ? any ideas how to achieve thatOverdress
S
3

http://pydoc.net/websocket-client/0.46.0/websocket._http/

Not sure what's going on with the newer version of websocket-client (snippet from 0.51.0), but the older _http.py (linked) had an if clause and the new one just relies on the environment variable. Unfortunately, it's not listed in any documentation I read. Spent a long while troubleshooting to find this bit, and then I found this page. Seems like websocket-client could do a better job handling this.

def _ssl_socket(sock, user_sslopt, hostname):
sslopt = dict(cert_reqs=ssl.CERT_REQUIRED)
sslopt.update(user_sslopt)

certPath = os.environ.get('WEBSOCKET_CLIENT_CA_BUNDLE')
if certPath and os.path.isfile(certPath) \
        and user_sslopt.get('ca_certs', None) is None \
        and user_sslopt.get('ca_cert', None) is None:
    sslopt['ca_certs'] = certPath
elif certPath and os.path.isdir(certPath) \
        and user_sslopt.get('ca_cert_path', None) is None:
    sslopt['ca_cert_path'] = certPath
Sacramentarian answered 30/8, 2018 at 13:11 Comment(0)
T
1

check from where ssl is picking the certificate.

import ssl
ssl.get_default_verify_paths()
>> DefaultVerifyPaths(cafile='/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl/cert.pem', capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/Library/Frameworks/Python.framework/V)

or

import _ssl
_ssl.get_default_verify_paths()
>>('SSL_CERT_FILE', '/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl/cert.pem', 'SSL_CERT_DIR', '/Library/Frameworks/Python.framework/Versions/3.8/etc/openssl/certs')

check if certificate file exists there, if not then download and save certificate there. You can download certificate from certifi library.

import certifi
certifi.where()
>> /venv/lib/python3.8/site-packages/certifi/cacert.pem

You can copy and save the file in default location.

Other solution, not tested: you can set environment variable SSL_CERT_FILE to path of certificate

Telemark answered 16/2, 2023 at 8:25 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.