Bug 32246 - Multiple connection attempts to a WebSocket server should not be allowed
Summary: Multiple connection attempts to a WebSocket server should not be allowed
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
: 55178 (view as bug list)
Depends on:
Blocks:
 
Reported: 2009-12-07 14:37 PST by Alexey Proskuryakov
Modified: 2016-03-31 11:15 PDT (History)
9 users (show)

See Also:


Attachments
Patch (30.72 KB, patch)
2013-04-12 08:51 PDT, Lamarque V. Souza
ap: review-
buildbot: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Alexey Proskuryakov 2009-12-07 14:37:08 PST
We don't have the following implemented yet:

-------------------------------
   1.   If the user agent already has a Web Socket connection to the
        remote host (IP address) identified by /host/, even if known by
        another name, wait until that connection has been established or
        for that connection to have failed.

        NOTE: This makes it harder for a script to perform a denial of
        service attack by just opening a large number of Web Socket
        connections to a remote host.

        NOTE: There is no limit to the number of established Web Socket
        connections a user agent can have with a single remote host.
        Servers can refuse to connect users with an excessive number of
        connections, or disconnect resource-hogging users when suffering
        high load.
-------------------------------
Comment 1 Fumitoshi Ukai 2009-12-07 21:52:32 PST
Chromium implement this in SocketStreamHandle, but it's not clean, but it needs to sniff data to detect handshake message.

Maybe, we need to add some methods in SocketStreamHandle and SocketStreamHandleClient.

 - new SocketStreamHandle




(In reply to comment #0)
> We don't have the following implemented yet:
> 
> -------------------------------
>    1.   If the user agent already has a Web Socket connection to the
>         remote host (IP address) identified by /host/, even if known by
>         another name, wait until that connection has been established or
>         for that connection to have failed.
> 
>         NOTE: This makes it harder for a script to perform a denial of
>         service attack by just opening a large number of Web Socket
>         connections to a remote host.
> 
>         NOTE: There is no limit to the number of established Web Socket
>         connections a user agent can have with a single remote host.
>         Servers can refuse to connect users with an excessive number of
>         connections, or disconnect resource-hogging users when suffering
>         high load.
> -------------------------------
Comment 2 Fumitoshi Ukai 2009-12-07 21:58:49 PST
Chromium implement this in SocketStreamHandle, but it's not clean.
It needs to sniff data to detect handshake message.

To implement it in WebCore/websocket,  we need to add some methods in SocketStreamHandle and SocketStreamHandleClient.

 - new SocketStreamHandle
        resolve ip address from host.
        calls back client->willOpen(handle, addresslist);
 - in WebSocketChannel, maintain table of address that is running handshake.
   if handle's address is open, call handle->connect().
   otherwise, wait other handle's handshake finishes, or close.

  - in WebSocketChannel, once handshake is finished or closed, clear its addresses from table.
     pick next handle which address becomes free from the table, and call handle->connect().



(In reply to comment #0)
> We don't have the following implemented yet:
> 
> -------------------------------
>    1.   If the user agent already has a Web Socket connection to the
>         remote host (IP address) identified by /host/, even if known by
>         another name, wait until that connection has been established or
>         for that connection to have failed.
> 
>         NOTE: This makes it harder for a script to perform a denial of
>         service attack by just opening a large number of Web Socket
>         connections to a remote host.
> 
>         NOTE: There is no limit to the number of established Web Socket
>         connections a user agent can have with a single remote host.
>         Servers can refuse to connect users with an excessive number of
>         connections, or disconnect resource-hogging users when suffering
>         high load.
> -------------------------------
Comment 3 Alexey Proskuryakov 2009-12-08 13:09:20 PST
This sounds like a good plan.

Alternatively, we could try to shoot down this provision in the spec. I'm not sure if it really adds any protection.
Comment 4 Alexey Proskuryakov 2009-12-08 14:02:57 PST
Actually, I need to reverse both statements!

Yes, WebSocket is the first way to open an unlimited number of connections to a single server, so it indeed likely needs additional protection to prevent DOS attacks.

But we don't really have a way to implement this correctly. Since each DNS request can result in a new result (this is a form of load balancing), resolving the name first and re-resolving it for actual connect() won't work. We don't have a way to pass both host name and its pre-resolved IP address down to the network stack.
Comment 5 Alexey Proskuryakov 2009-12-08 17:12:18 PST
<rdar://problem/7455142>
Comment 6 Alexey Proskuryakov 2011-02-24 22:45:33 PST
*** Bug 55178 has been marked as a duplicate of this bug. ***
Comment 7 Joe Mason 2011-03-02 11:09:57 PST
(In reply to comment #4)
> Actually, I need to reverse both statements!
> 
> Yes, WebSocket is the first way to open an unlimited number of connections to a single server, so it indeed likely needs additional protection to prevent DOS attacks.
> 
> But we don't really have a way to implement this correctly. Since each DNS request can result in a new result (this is a form of load balancing), resolving the name first and re-resolving it for actual connect() won't work. We don't have a way to pass both host name and its pre-resolved IP address down to the network stack.

We could add didResolveIP(handle, ip) to SocketStreamHandleClient, and only add a host/ip pair to the "existing connections" map if this is called.  That way each platform's network backend can call this function if they can get the IP, and if they don't implement it they just don't get connection limiting.
Comment 8 Lamarque V. Souza 2013-04-12 08:51:30 PDT
Created attachment 197853 [details]
Patch

Proposed patch. The patch is not ready so I am not asking for +cq just yet. I want to read comments about it before I finish it, specially regarding the RefPtr/ListHashSet usage and the two FIXME I added to it. Just as a note, I am using 50 as pending request limit because LayoutTests/http/tests/websocket/tests/hybi/multiple-connections.html starts 50 connection attempts. Using 50 as limit allows me to avoid editing multiple-connections.html to lower that value.
Comment 9 Build Bot 2013-04-12 09:15:35 PDT
Comment on attachment 197853 [details]
Patch

Attachment 197853 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-queues.appspot.com/results/131073
Comment 10 Build Bot 2013-04-12 09:40:02 PDT
Comment on attachment 197853 [details]
Patch

Attachment 197853 [details] did not pass mac-ews (mac):
Output: http://webkit-queues.appspot.com/results/35303
Comment 11 Alexey Proskuryakov 2013-04-12 10:56:59 PDT
Comment on attachment 197853 [details]
Patch

I don't think that WebSocket class is the correct level for such checks. In a multi-process environment, the checks should be in networking process to account for the possibility of a client using multiple windows and/or frames.
Comment 12 Lamarque V. Souza 2013-04-12 14:32:05 PDT
(In reply to comment #11)
> (From update of attachment 197853 [details])
> I don't think that WebSocket class is the correct level for such checks. In a multi-process environment, the checks should be in networking process to account for the possibility of a client using multiple windows and/or frames.

Would moving the checks to WebSocketChannel as suggested in comment #2 work in multi-process environment?
Comment 13 Alexey Proskuryakov 2013-04-15 10:26:40 PDT
Unsure. I really see this as part of underlying networking code, just like HTTP connection limit enforcement.
Comment 14 Lamarque V. Souza 2013-04-15 12:15:15 PDT
(In reply to comment #13)
> Unsure. I really see this as part of underlying networking code, just like HTTP connection limit enforcement.

In which file is the HTTP connection limit enforcement implemented? I have found initializeMaximumHTTPConnectionCountPerHost in Source/WebCore/platform/network/ResourceRequestBase.h but it seems not to be used anywhere.
Comment 15 Joe Mason 2013-04-15 12:28:49 PDT
(In reply to comment #14)
> (In reply to comment #13)
> > Unsure. I really see this as part of underlying networking code, just like HTTP connection limit enforcement.
> 
> In which file is the HTTP connection limit enforcement implemented? I have found initializeMaximumHTTPConnectionCountPerHost in Source/WebCore/platform/network/ResourceRequestBase.h but it seems not to be used anywhere.

It's not in WebKit - it's part of the underlying platform network stack (the Windows network stack, or the Mac network stack, or libsoup for the GTK+ port, or the Qt network classes for the Qt port, etc.)

I'm not sure websockets are in the same boat as HTTP connection limits. It's assumed that the underlying stack is a complete implementation of the HTTP protocol, but for WebSockets most of the protocol is implemented in WebKit and the "underlying stack" is simply TCP. So any additional limitations that don't apply to TCP connections in general would be more properly be part of the WebSocket protocol code in WebKit than in the network stack.

(To be a completely clean parallel to HTTP, much of the WebSocket code should be removed from webkit and provided through an external library... but that's uselessly pedantic.)
Comment 16 Lamarque V. Souza 2013-04-15 14:05:21 PDT
(In reply to comment #15)
> (In reply to comment #14)
> > In which file is the HTTP connection limit enforcement implemented? I have found initializeMaximumHTTPConnectionCountPerHost in Source/WebCore/platform/network/ResourceRequestBase.h but it seems not to be used anywhere.
> 
> It's not in WebKit - it's part of the underlying platform network stack (the Windows network stack, or the Mac network stack, or libsoup for the GTK+ port, or the Qt network classes for the Qt port, etc.)
> 
> I'm not sure websockets are in the same boat as HTTP connection limits. It's assumed that the underlying stack is a complete implementation of the HTTP protocol, but for WebSockets most of the protocol is implemented in WebKit and the "underlying stack" is simply TCP. So any additional limitations that don't apply to TCP connections in general would be more properly be part of the WebSocket protocol code in WebKit than in the network stack.
> 
> (To be a completely clean parallel to HTTP, much of the WebSocket code should be removed from webkit and provided through an external library... but that's uselessly pedantic.)

Actually, initializeMaximumHTTPConnectionCountPerHost is used in Source/WebCore/loader/ResourceLoadScheduler.cpp. It seems to be the right place. I am still investigating how ResourceLoadScheduler works.
Comment 17 Alexey Proskuryakov 2013-04-15 14:20:28 PDT
WebCore counts HTTP connections in addition to lower level code doing this. IIRC that's a workaround for some CFNetwork issues, where trying to exceed allowed number of connections would trigger bugs. I don't remember all details though.