openwallet-foundation / acapy

ACA-Py is a foundation for building decentralized identity applications and services running in non-mobile environments.
https://aca-py.org
Apache License 2.0
421 stars 514 forks source link

[Security] WebSocket endpoint accepts unauthorized connections #1727

Open tdiesler opened 2 years ago

tdiesler commented 2 years ago

The default WebSocket endpoint ws://localhost:8031/ws accepts unauthorized connections, which allows rough clients to create large numbers of connections and such slowing the agent down (i.e. DOS attack)

@Test
void tesUnauthorized() throws Exception {

    CountDownLatch latch = new CountDownLatch(2);

    WebSocketEventHandler handler = new WebSocketEventHandler() {
        @Override
        public void handleRaw(String topic, String message) {
            log.info("{}: {}", topic, message);
            latch.countDown();
        }
    };

    WebSocket webSocket = getHttpClient().newWebSocket(new Request.Builder()
            .url("ws://localhost:8031/ws")
            //.header("X-API-Key", ACAPY_API_KEY)
            //.header("Authorization", "Bearer " + token)
            .build(), new WebSocketListener(null, handler));

    try {

        Assertions.assertFalse(latch.await(10, TimeUnit.SECONDS), "No messages expected");

    } finally {
        closeWebSocket(webSocket);
    }
}

2022-04-13 09:30:14 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - settings: {"authenticated":false,"label":"Aries Cloud Agent","endpoint":"http://localhost:8030","no_receive_invites":false,"help_link":null}
2022-04-13 09:30:19 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - ping: null

See UnauthorizedWebSocketTest

swcurran commented 2 years ago

@andrewwhitehead and @TimoGlastra (and others) -- thoughts on this? I believe the primary use of the websockets transport on the ACA-Py side is as a mediator. Thoughts on how to handle this?

etschelp commented 2 years ago

I'm also testing websockets in multi tenant mode and what I noticed is the following during ws connection setup:

  1. No admin key and no bearer token set, directly returns "unauthorised" which is what I would expect
  2. Only bearer token set, allows to connect but only the settings event is received
  3. Set both the admin key and a bearer token from any of the subwallets receives all events from all of the subwallets regardless of the wallet dispatch type. I do not know if this is expected.

From what I have seen so far the websocket pretty much behaves as the webhook, at least for single tenant wallets. I hope it is supposed to work that way because it's pretty amazing stuff when writing tests.

swcurran commented 2 years ago

@TimoGlastra @ianco -- is that behaviour for 2 and 3 in the previous note what you would expect? This is way beyond my knowledge of multi-tenant.

ianco commented 2 years ago

@TimoGlastra @ianco -- is that behaviour for 2 and 3 in the previous note what you would expect? This is way beyond my knowledge of multi-tenant.

I don't know, I haven't done much work (i.e. any) with websockets. Let me know if you want me to dig into it.

tdiesler commented 2 years ago

This code, which is now independent of the acapy-java-client, shows a successful WS handshake and the Open message with the Protocol Update - no admin key nor bearer token is set. We then also see the "settings" message from Aca-Py

2022-04-22 11:03:07 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - Open: Response{protocol=http/1.1, code=101, message=Switching Protocols, url=http://localhost:8031/ws}
2022-04-22 11:03:07 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - Message: {"topic": "settings", "payload": {"authenticated": false, "label": "Aries Cloud Agent", "endpoint": "http://localhost:8030", "no_receive_invites": false, "help_link": null}}
2022-04-22 11:03:07 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - Closing: 1000 
2022-04-22 11:03:07 INFO  [io.nessus.aries.test.websocket.UnauthorizedWebSocketTest] - Closed: 1000 
tdiesler commented 2 years ago

That sub-wallet specific WS connections (i.e.connections that were created with the wallet's access token) see messages for other sub-wallets, may possibly be related to #1742. For example, Acme sees connections handshake messages between Alice and Faber.

tdiesler commented 2 years ago

From the websockets page

An attacker who can open an arbitrary number of connections will be able to perform a denial of service by memory exhaustion. If you’re concerned by denial of service attacks, you must reject suspicious connections before they reach websockets, typically in a reverse proxy.