Closed jca-klk closed 1 year ago
Thanks, I'm having a look.
On first glance, while this is definitely bad and needs to get fixed, I don't see how this would go unnoticed, because it'd need an equally broken server that takes unencrypted connections on an encrypted port, so I'm not treating this as security critical (the severity would be minimal, given it'd need an already compromised peer). If you disagree, please feel free to file for a CVE number for this, and report it here.
That websockets behaves subtly different from OpenSSL is regrettable. It should be straightforward to fix now that you have identified the issue, although it saddens me that I'll have to add the explicit code to use the system's CA storage (which is a choice I don't like for security reasons but which makes sense for practical reasons, and OpenSSL's defaults meant that I was deferring to their defaults rather than recommending system CAs myself -- anyhow, this needs fixing).
Hi! Thanks for the quick reply.
No, I do not think this is a security issue. The reverse-proxy replies with HTTP/1.0 400 Bad Request
with data Client sent an HTTP request to an HTTPS server.
It does not go through the proxy and the client closes the TCP connection right away.
My issue is indeed on the functional part, with default settings.
I already encountered such issue with the websockets
module, and I chose to only pass ssl
parameter when required, using something like that:
wsargs = {}
if ssl_is_required:
wsargs['ssl'] = my_ssl_context
ws = await websockets.connect(uri, **wsargs)
I am not sure it is easy to do in here. I feel I miss some context after reading commit 6270d9382a1589a7d228eb34a91607bdc625dc60
Could you give #326 a try? It should fix this.
For context on the change that broke this: aiocoap can also run in the browser, in which case it can only use WebSockets connections, and they exit the browser through a websocket module that just calls out to the browser's JavaScript WebSocket API. That one is not implemented using the ssl module, and has no influence on which certificates are accepted.
Tests couldn't really catch the breakage because no certificates created for a test situation will be valid under the default (system-wide) context, unless the tests install their fake CA into the system root, which I'd consider excessive.
I confirm #326 works with my setup.
Thank you for the quick responses and fix. Kudos 🎉
Indeed that should have been is not
-- pyodide isn't quite easy to test, and I haven't gotten to running it so far.
Once I'm done with those tests, this will be merged.
Thanks for reporting this, and your quick feedback!
Now tested on pyodide:
import micropip
await micropip.install("https://some-server-that-has-the-right-CORS-headers/aiocoap-0.4.7.post0-py3-none-any.whl")
from aiocoap import *
ctx = await Context.create_client_context()
req = Message(code=GET, uri="coaps+ws://rd.coap.amsuess.com/.well-known/core")
await ctx.request(req).response
Hello!
I am experimenting with aiocoap and WebSockets over TLS (sheme
coaps+ws://
).The basic client (
/clientGET.py
) fails when run withcoaps+ws
against the basic server (/server.py
) configured behind a reverse-proxy. The server behaves correctly when I connect with a basicwebsockets
module client.A Wireshark capture showed that
websockets
attempts to connect to the server using clear-text HTTP, despite of the scheme. The TCP port, however, was correctly set to 443.I dug down the issue to
transports/ws.py
_connect_task
wheressl_context
is set toNone
. Passingssl=None
towebsockets.connect()
makes it not create the TLS context withcreate_default_context()
.websockets documentation on websockets.client.connect:
In this case,
ssl
is provided, but set to None by default.It seems that the issue comes from credentials.py: CredentialsMap.ssl_client_context() which returns
None
when no credentials are configured, and expects the "user to fill in ssl.create_default_context()".I believe aiocoap users should not have to configure any specific credentials when connecting to websockets over TLS, but rather have sane default as
websockets
module provides, i.e.ssl.create_default_context()
somehow.I am not sure how to fix this bug though, as I am not familiar with this library and how it handles Credentials.
Steps to reproduce
Server side
On a Linux machine with a public IP address and associated domain name, I ran the Caddy server as a TLS endpoint and reverse-proxy using the following Caddyfile. (Replace
coap.example.com
with the machine domain name). Caddy's reverse-proxy automatically handles websockets, and handles also TLS certificate request.On the same server, I ran the example server
server.py
with no changes.Client side
I ran the
clientGET.py
changing theMessage()
uri
tocoaps+ws://coap.example.com/time
. I also enabled DEBUG logging.It fails as follow (hostname redacted):
Wireshark capture of the request shows that something is wrong: "Dangerous misconfiguration".
Resolution
Commenting out
transports/ws.py
line 286 altogether "fixes" the problem, but I guess there is a better solution.Extra information