I have read the docs up and down and I can't seem to find a reference for disabling SSL certificate verification. As of right now, I currently have a project where I am doing an intentional man-in-the-middle attack to switch proxies on need-bases.
client <-> Proxy Switcher (server acting as proxy)
Proxy Switcher (emulated client) <-> Exchanges
On the return of anything but a 200
, the proxy switcher automatically switches proxy and try again. A man-in-the-middle is needed to verify that the HTTPS requests are coming back with the proper headers, and this part seems to work fine. However, communicating between the client and proxy switcher seems to be returning back (expected) SSL certificate issues, as proxy switcher automatically generates its own local certificate. This, though, I would like to disable.
Without adding the certificate to my local trust env, I receive the error:
(Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')])")))
Which is warranted, but would be best if could be disabled by some exchange flag. When adding the certificate to my local trust env, I receive the error
(Caused by SSLError(SSLCertVerificationError("hostname 'pro.coinbase.com' doesn't match 'Felipes-MacBook-Pro.local'")))
Which is also warranted, but a check I would much rather disable. Any help would be appreciated. My idea would be something simple as:
ex = getattr(ccxt, exchange)(
{
"session": cfscrape.create_scraper(),
"enableRateLimit": False,
"verify_ssl_certificates": False,
}
)
Note: I have tried the verify
flag with no success.
@synchronizing
@synchronizing ↓ does this help?
session = cfscrape.create_scraper()
session.verify = False
ex = getattr(ccxt, exchange)(
{
"session": session,
"enableRateLimit": False,
}
)
@synchronizing
- What's your version of Python?
- Are you using the sync or the async version of the lib?
Python 3.7.2 and using sync (tested with cfscrape, and without). Will give it a try on async as well, just to be sure, as I know aiohttp
has fewer SSL verifications.
@synchronizing let us know if the comment above does not resolve the issue for you: https://github.com/ccxt/ccxt/issues/5394#issuecomment-506918563
@synchronizing ↓ does this help?
session = cfscrape.create_scraper() session.verify = False ex = getattr(ccxt, exchange)( { "session": session, "enableRateLimit": False, } )
Tested with the above with the same SSL verification error, unfortunately.
Tested with ccxt_async
, and it seems to work fine -- passes through the proxy finder as well, without throwing back any issue. SSL certificated added in env (not sure if it would work without.)
However, cfscrape
is still desired for coinbasepro.
This page says it should have worked with the sync version as well: https://2.python-requests.org/en/master/user/advanced/#ssl-cert-verification
@synchronizing can you post a complete short snippet of your code to reproduce it, say, 10-20 lines? We need to make sure that there's no other interference, therefore we need a complete snippet, including the instantiation.
Sure thing @kroitor -- as of now the code is wrapped in an API server, so give me a few to extract the relevant lines to a separate text file for easy testing on your end.
Also: is the session.verify
a boolean, or a certificate location string? From previous issues I saw here, I thought it was simply a bool flag.
Also: is the session.verify a boolean, or a certificate location string? From previous issues I saw here, I thought it was simply a bool flag.
It should work either way, as a boolean or as a string-path, if the docs are correct.
Also: is the session.verify a boolean, or a certificate location string? From previous issues I saw here, I thought it was simply a bool flag.
It should work either way, as a boolean or as a string-path, if the docs are correct.
Sounds good. Give me a few to compile the problem down to a few lines of code.
Utilizing the following code:
import ccxt
exchange = ccxt.binance()
exchange.session.verify = False # With, or without line.
fetch = exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
print(fetch)
With export to a man-in-the-middle proxy:
export http_proxy=http://127.0.0.1:8888
export https_proxy=http://127.0.0.1:8888
I still receive error on the sync version of ccxt
. If you would like to test it out with the man-in-the-middle, you can find it on my repo here. Just run the example/example_server.py
file.
I just came to realize the reason it might have worked with aiohttp
is that even with setting exchange.session.trust_env
and exchange.session.trust_env_aiohttp
, neither flags respect the set http_proxy
and https_proxy
env variable.
Update: aiohttp
does not work either.
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
print(asyncio.get_event_loop().run_until_complete(asyncio.gather(fetch_stuff())))
With implicit aiohttp_proxy
set (since http_proxy
and https_proxy
is does not seem to be respected), the SSL checks still return error. I know for a fact aiohttp
uses the flag ssl
(rather than verify
, like the requests
library, to enable/disable SSL checks) but I assume there is no existing flag for ccxt
.
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
↑ This is not a correct way of configuring it. You should add an async session and set verify = False
on it, before passing it to the binance constructor. The derived exchange class does not support the verify
option.
More about it here:
It sync-misbehavior might be a bug in the MITM proxy: https://www.google.com/search?q=python+https+proxy+ssl+verify+requests. Looks like you're not the only person having difficulties when using proxies + ssl verify.
async def fetch_stuff(): exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False}) fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000) await exchange.close() return fetch
↑ This is not a correct way of configuring it. You should add an async session and set
verify = False
on it, before passing it to the binance constructor. The derived exchange class does not support theverify
option.
When you say this, would this be the correct format?
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888"})
exchange.session.verify = False
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
print(asyncio.get_event_loop().run_until_complete(asyncio.gather(fetch_stuff())))
It might be a bug in the MITM proxy: https://www.google.com/search?q=python+https+proxy+ssl+verify+requests. Looks like you're not the only person having difficulties when using proxies + ssl verify.
Tell me about it -- SLL + proxies is a nightmare, as I've come to find out 😩. However, mitm
is acting as the destination server for the client, so it's not actually communicating the request forward to the destination server as a normal proxy would. Instead, it initiates an emulated client that does that, and then returns the request of the emulated client back to the actual client, reading the request in the middle. The issue is the initial communication between mitm
and the client with ccxt
, mainly due to a self-signed certificate in the middle. Even with verify
flag set to false, the error seems to persist.
would this be the correct format?
Nope. This would be the correct format:
import aiohttp
import asyncio
event_loop = asyncio.get_event_loop()
async def fetch_stuff():
connector = aiohttp.TCPConnector(ssl=False, loop=event_loop)
session = aiohttp.ClientSession(loop=event_loop, connector=connector, trust_env=True)
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", 'session': session})
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
print(event_loop.run_until_complete(asyncio.gather(fetch_stuff())))
Does that help?
would this be the correct format?
Nope. This would be the correct format:
import aiohttp import asyncio event_loop = asyncio.get_event_loop() async def fetch_stuff(): trust_environment_variables = False connector = aiohttp.TCPConnector(ssl=False, loop=event_loop) session = aiohttp.ClientSession(loop=event_loop, connector=connector, trust_env=trust_environment_variables) exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", 'session': session}) fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000) await exchange.close() return fetch print(event_loop.run_until_complete(asyncio.gather(fetch_stuff())))
Does that help?
Yes, it did! I received a request in the mitm
which got processed but didn't get properly returned (which is fault from mitm
). Much appreciated man! Let me give it a try with sync ccxt
.
@synchronizing something like the above should work for the sync
version as well. Just need to dig the internets on the proper way to configure it. However, this is beyond CCXT, unfortunately.
@synchronizing something like the above should work for the
sync
version as well. Just need to dig the internets on the proper way to configure it. However, this is beyond CCXT, unfortunately.
Perfectly understandable -- all I was hoping for was a proper CONNECT
and GET
with mitm
, as from there on out I knew ccxt
had completed the proper ssl steps.
@synchronizing are you ok if we close this for now?
Forgive me for the repetitive questioning: but with cfscraper
and standard sync functionality, can we set a session
? While I have you on the wire.
If this is beyond the project due to cfscraper
, no worries. The help above has already been fantastical and I truly do appreciate it.
Also, completely irrelevant: Don't fall for AdBlocker Pro
-- they were bought out by some company a while ago, and still display advertisements. Open source solution is uBlock Origin which very few people seem to be aware is out there and is also a superb blocker in comparison (including YouTube advertisement, thank the Lord.)
Forgive me for the repetitive questioning: but with cfscraper and standard sync functionality, can we set a session?
Yes, that should be possible. However, there may be bugs outside of CCXT:
I will add the exchange.verify
option for the sync
version shortly and will need your help to test it against your proxy.
Forgive me for the repetitive questioning: but with cfscraper and standard sync functionality, can we set a session?
Yes, that should be possible. However, there may be bugs outside of CCXT:
I will add the
exchange.verify
option for thesync
version shortly and will need your help to test it against your proxy.
Sounds like a plan! Again, I appreciate the help. Feel free to close this issue and re-open when I am needed for testing.
@synchronizing should not take too long, will let you know when it's there, 30-60 minutes.
@synchronizing should not take too long, will let you know when it's there, 30-60 minutes.
Sounds good with me, I'll be here.
Ok, here's what we need to do:
# sync
import ccxt
exchange = ccxt.binance({'verify': False})
fetch = exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
print(fetch)
or
# async
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
We will be happy if you let us know whether it did solve the issue for you or not.
We will be happy if you let us know whether it did solve the issue for you or not.
Ofc, will be happy to help. For the async
, intended feature for assigning verify
directly on binance
constructor?
For the async, intended feature for assigning verify directly on binance constructor?
Yep, I've added the verify
constructor-option to both the sync
and the async
version. You can also set it afterwards after creating the exchange instance:
exchange = ccxt.binance()
exchange.verify = False
For the async, intended feature for assigning verify directly on binance constructor?
Yep, I've added the
verify
option to both thesync
and theasync
version.
Beautiful. Will give it a shot right now.
@synchronizing found a couple of minor issues along the way, added the fixes, so, it will arrive shortly (5-10 minutes). Standing by.
@synchronizing found a couple of minor issues along the way, added the fixes, so, it will arrive shortly (5-10 minutes). Standing by.
What version should I be on the look out for?
@synchronizing 1.18.844 (the one upcoming).
Ok, it has arrived, looking forward to hearing from you.
sync
version is working as expected on 1.18.844
:
import ccxt as ccxt
exchange = ccxt.binance(
{
"proxies": {"http": "http://127.0.0.1:8888", "https": "http://127.0.0.1:8888"},
"verify": False,
}
)
fetch = exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
print(fetch)
async
version does not work with the following:
import ccxt.async_support as ccxt
import asyncio
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
print(asyncio.get_event_loop().run_until_complete(asyncio.gather(fetch_stuff())))
SSL error is thrown for the above code. However, it does work when loading session
as shown previously here. Assumingly, verify
might not be setting the ssl
flag in the TCPConnector
to False
internally within ccxt
.
Assumingly, verify might not be setting the ssl flag in the TCPConnector to False internally within ccxt.
Ok, I've added one more edit to it, let us know if 1.18.845 does not resolve the issue on the async
side. Closing this for now. Feel free to reopen it or just ask further questions, if any. We will be happy if you report back anyways.
Sounds good -- will give it a try, thank you again.
async
is still not working with 1.18.845
with "verify" : False
set.
import ccxt.async_support as ccxt
import asyncio
async def fetch_stuff():
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
print(exchange.verify)
fetch = await exchange.fetch_ohlcv("ETH/BTC", "1m", 1514764800000)
await exchange.close()
return fetch
print(asyncio.get_event_loop().run_until_complete(asyncio.gather(fetch_stuff())))
@synchronizing doesn't work with verify: False
, but works with https://github.com/ccxt/ccxt/issues/5394#issuecomment-506921151 ?
@synchronizing doesn't work with
verify: False
, but works with #5394 (comment) ?
Correct, unfortunately.
@synchronizing are you sure about that? This is strange, because they should be technically equal...
@synchronizing ah, nvm, found another bug there with the ordering of the calls, will fix it in a moment.
@synchronizing are you sure about that? This is strange, because they should be technically equal...
They seem to be different on further testing:
import ccxt.async_support as ccxt
import asyncio
import aiohttp
async def fetch_stuff():
connector = aiohttp.TCPConnector(ssl=False, loop=asyncio.get_event_loop())
exchange = ccxt.binance({"aiohttp_proxy": "http://127.0.0.1:8888", "verify": False})
print(exchange.session._connector._ssl)
print(connector._ssl)
await exchange.close()
asyncio.get_event_loop().run_until_complete(fetch_stuff())
Outputs:
<ssl.SSLContext object at 0x10f55c480>
False
@synchronizing ah, nvm, found another bug there with the ordering of the calls, will fix it in a moment.
Awesome, lmk!
@synchronizing ok, let's try again with 1.18.846. Unfortunately, I can't really test it on my side atm, your help with debugging it is very much appreciated! The autobuild takes 10-15 minutes.
@synchronizing ok, let's try again with 1.18.846. Unfortunately, I can't really test it on my side atm, your help with debugging it is very much appreciated! The autobuild takes 10-15 minutes.
My pleasure man -- I appreciate all the work on your end. I'll be on the lookout for the new release to give it a try.
Working as expected! Thank you again, cheers.
@kroitor
Should we log a warning if SSL Certificates are disabled or overwritten, since this can result in stolen keys? MITM Attack and there are some exchanges using login-data like keys (for example dx.exchange).
@kroitor
Should we log a warning if SSL Certificates are disabled or overwritten, since this can result in stolen keys? MITM Attack and there are some exchanges using login-data like keys (for example dx.exchange).
Let me just add:
aiohttp
(ccxt.async_support
) does not throw out SSL certificate warning.requests
(ccxt
) does throw SSL certificate warning through urllib3
. @brandsimon in this particular case, the MITM attack is done deliberately by the owner. But in general, yes, I think it would be great to have a warning so that we keep people aware! )
@synchronizing thx for the hints!
Most helpful comment
https://travis-ci.org/ccxt/ccxt/builds