Title: [264306] trunk
Revision
264306
Author
[email protected]
Date
2020-07-13 10:57:59 -0700 (Mon, 13 Jul 2020)

Log Message

Add artificial delay to WebSocket connections to mitigate port scanning attacks
https://bugs.webkit.org/show_bug.cgi?id=213143
<rdar://problem/64308927>

Reviewed by Alex Christensen.

Source/WebKit:

When establishing web socket connections, closed ports typically
return an error more quickly than open ports due to the additional
time it takes open ports to perform a TLS handshake. This patch
adds a delay to the closed-port case to prevent distinguishing these
cases.

Test: http/tests/websocket/tests/hybi/closed-port-delay.html

* NetworkProcess/NetworkSocketStream.cpp:
(WebKit::NetworkSocketStream::NetworkSocketStream):
(WebKit::randomDelay):
Add a random delay between 10 and 100ms before sending IPC.

(WebKit::NetworkSocketStream::sendDelayedFailMessage):
(WebKit::NetworkSocketStream::didFailSocketStream):
Only delay for the proper error code indicating the connection
was refused for a closed port.

* NetworkProcess/NetworkSocketStream.h:

LayoutTests:

Added layout test coverage, making sure closed ports always experience
a delay of at least 10ms.

* TestExpectations:
The added delay code is only present in WebKit, not WebKitLegacy.
Additionally, web sockets behave differently on Windows, so this test
should be skipped on all platforms and only enabled for mac and ios wk2.

* http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt:
With the added delay, this test will only receive one console message.
To confirm there will be no flakiness, I ran this test for 500
iterations and ensured they passed.
* http/tests/websocket/tests/hybi/closed-port-delay-expected.txt:
* http/tests/websocket/tests/hybi/closed-port-delay.html: Added.
* platform/wk2/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (264305 => 264306)


--- trunk/LayoutTests/ChangeLog	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/LayoutTests/ChangeLog	2020-07-13 17:57:59 UTC (rev 264306)
@@ -1,3 +1,27 @@
+2020-07-13  Kate Cheney  <[email protected]>
+
+        Add artificial delay to WebSocket connections to mitigate port scanning attacks
+        https://bugs.webkit.org/show_bug.cgi?id=213143
+        <rdar://problem/64308927>
+
+        Reviewed by Alex Christensen.
+
+        Added layout test coverage, making sure closed ports always experience
+        a delay of at least 10ms.
+
+        * TestExpectations:
+        The added delay code is only present in WebKit, not WebKitLegacy.
+        Additionally, web sockets behave differently on Windows, so this test
+        should be skipped on all platforms and only enabled for mac and ios wk2.
+
+        * http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt:
+        With the added delay, this test will only receive one console message.
+        To confirm there will be no flakiness, I ran this test for 500
+        iterations and ensured they passed.
+        * http/tests/websocket/tests/hybi/closed-port-delay-expected.txt: 
+        * http/tests/websocket/tests/hybi/closed-port-delay.html: Added.
+        * platform/wk2/TestExpectations:
+
 2020-07-13  Keith Miller  <[email protected]>
 
         Clean up SourceProvider and add caller relative load script to jsc.cpp

Modified: trunk/LayoutTests/TestExpectations (264305 => 264306)


--- trunk/LayoutTests/TestExpectations	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/LayoutTests/TestExpectations	2020-07-13 17:57:59 UTC (rev 264306)
@@ -4367,3 +4367,5 @@
 # Only Cocoa ports support the -apple-system font keywords
 fast/text/text-styles/-apple-system [ ImageOnlyFailure ]
 
+# Artificial web socket delays are only supported on wk2. 
+http/tests/websocket/tests/hybi/closed-port-delay.html [ Skip ]

Modified: trunk/LayoutTests/http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt (264305 => 264306)


--- trunk/LayoutTests/http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/LayoutTests/http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt	2020-07-13 17:57:59 UTC (rev 264306)
@@ -1,5 +1,4 @@
 CONSOLE MESSAGE: WebSocket network error: The operation couldn’t be completed. Connection refused
-CONSOLE MESSAGE: WebSocket network error: The operation couldn’t be completed. Connection refused
 Construct a cross-site WebSocket in a frame with server-side refusal. The test passes if Resource Load Statistics logs it properly.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

Copied: trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay-expected.txt (from rev 264305, trunk/LayoutTests/http/tests/websocket/connection-refusal-in-frame-resource-load-statistics-expected.txt) (0 => 264306)


--- trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay-expected.txt	2020-07-13 17:57:59 UTC (rev 264306)
@@ -0,0 +1,13 @@
+CONSOLE MESSAGE: WebSocket network error: The operation couldn’t be completed. Connection refused
+CONSOLE MESSAGE: WebSocket network error: The operation couldn’t be completed. Connection refused
+CONSOLE MESSAGE: WebSocket network error: The operation couldn’t be completed. Connection refused
+Tests that WebSocket close report is delayed to mitigate port scanning attacks.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Successfully delayed reporting of closed WebSocket connection
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay.html (0 => 264306)


--- trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-port-delay.html	2020-07-13 17:57:59 UTC (rev 264306)
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+description("Tests that WebSocket close report is delayed to mitigate port scanning attacks.");
+
+jsTestIsAsync = true;
+
+var countReceivedMessage = 0;
+
+async function scan_ports()
+{
+    // Scan multiple closed ports.
+    var result1 = await scan_port(8181);
+    var result2 = await scan_port(1234);
+    var result3 = await scan_port(3001);
+    if (result3 == "testPassed" && result2 == "testPassed" && result1 == "testPassed") {
+        testPassed("Successfully delayed reporting of closed WebSocket connection")
+        finishJSTest();
+    } else {
+        testFailed("Failed to delay closed WebSocket connection");
+        finishJSTest();
+    }
+}
+
+// Test based off of BeEF port scanning attack technique (https://github.com/beefproject/beef/blob/master/modules/network/port_scanner/command.js)
+function scan_port(port)
+{
+    return new Promise(function(resolve, reject) {
+        var url = "" + port;
+        var ws = new WebSocket(url);
+        var startTime = (new Date).getTime();
+        var timeout = 3000;
+
+        clearIntervalID = setInterval(
+        function ()
+        {
+            var interval = (new Date).getTime() - startTime;
+
+            if (ws.readyState === WebSocket.CONNECTING)
+            {
+            }
+
+            if (ws.readyState === WebSocket.OPEN)
+            {
+            }
+
+            if (ws.readyState === WebSocket.CLOSING)
+            {
+            }
+
+            if (ws.readyState === WebSocket.CLOSED)
+            {
+                clearInterval(clearIntervalID);
+                ws.close();
+                countReceivedMessage++;
+                if (interval < 10) {
+                    debug("Timer failed to delay report of closed state");
+                    resolve("testFailed");
+                } else
+                    resolve("testPassed");
+            }
+
+            if (interval >= timeout)
+            {
+                clearInterval(clearIntervalID);
+                debug("Connection timeout");
+                ws.close();
+                resolve("testFailed");
+            }
+        }
+        , 1);
+    });
+}
+
+scan_ports();
+
+</script>
+</body>
+</html>

Modified: trunk/LayoutTests/platform/wk2/TestExpectations (264305 => 264306)


--- trunk/LayoutTests/platform/wk2/TestExpectations	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/LayoutTests/platform/wk2/TestExpectations	2020-07-13 17:57:59 UTC (rev 264306)
@@ -802,3 +802,5 @@
 # Newly imported test that is flaky on WebKit2 only.
 imported/w3c/web-platform-tests/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/no_window_open_when_term_nesting_level_nonzero.window.html [ Pass Failure ]
 
+# Closed port web socket delays are only supported on wk2.
+http/tests/websocket/tests/hybi/closed-port-delay.html [ Pass ]

Modified: trunk/Source/WebKit/ChangeLog (264305 => 264306)


--- trunk/Source/WebKit/ChangeLog	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/Source/WebKit/ChangeLog	2020-07-13 17:57:59 UTC (rev 264306)
@@ -1,3 +1,31 @@
+2020-07-13  Kate Cheney  <[email protected]>
+
+        Add artificial delay to WebSocket connections to mitigate port scanning attacks
+        https://bugs.webkit.org/show_bug.cgi?id=213143
+        <rdar://problem/64308927>
+
+        Reviewed by Alex Christensen.
+
+        When establishing web socket connections, closed ports typically
+        return an error more quickly than open ports due to the additional
+        time it takes open ports to perform a TLS handshake. This patch
+        adds a delay to the closed-port case to prevent distinguishing these
+        cases.
+
+        Test: http/tests/websocket/tests/hybi/closed-port-delay.html
+
+        * NetworkProcess/NetworkSocketStream.cpp:
+        (WebKit::NetworkSocketStream::NetworkSocketStream):
+        (WebKit::randomDelay):
+        Add a random delay between 10 and 100ms before sending IPC.
+
+        (WebKit::NetworkSocketStream::sendDelayedFailMessage):
+        (WebKit::NetworkSocketStream::didFailSocketStream):
+        Only delay for the proper error code indicating the connection
+        was refused for a closed port.
+
+        * NetworkProcess/NetworkSocketStream.h:
+
 2020-07-13  Zan Dobersek  <[email protected]>
 
         [GTK][WPE] WTR and API-tests timing out after r264283

Modified: trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.cpp (264305 => 264306)


--- trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.cpp	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.cpp	2020-07-13 17:57:59 UTC (rev 264306)
@@ -44,6 +44,7 @@
     : m_identifier(identifier)
     , m_connection(connection)
     , m_impl(SocketStreamHandleImpl::create(url, *this, sessionID, credentialPartition, WTFMove(auditData), NetworkStorageSessionProvider::create(networkProcess, sessionID).ptr()))
+    , m_delayFailTimer(*this, &NetworkSocketStream::sendDelayedFailMessage)
 {
 }
 
@@ -101,10 +102,29 @@
     send(Messages::WebSocketStream::DidUpdateBufferedAmount(amount));
 }
 
+static const auto delayMaxMilliseconds = 100;
+static const double delayMinMilliseconds = 10;
+static const auto closedPortErrorCode = 61;
+
+static Seconds randomDelay()
+{
+    return Seconds::fromMilliseconds(delayMinMilliseconds + static_cast<double>(cryptographicallyRandomNumber() % delayMaxMilliseconds));
+}
+
+void NetworkSocketStream::sendDelayedFailMessage()
+{
+    send(Messages::WebSocketStream::DidFailSocketStream(m_closedPortError));
+}
+
 void NetworkSocketStream::didFailSocketStream(SocketStreamHandle& handle, const SocketStreamError& error)
 {
     ASSERT_UNUSED(handle, &handle == m_impl.ptr());
-    send(Messages::WebSocketStream::DidFailSocketStream(error));
+    
+    if (error.errorCode() == closedPortErrorCode) {
+        m_closedPortError = error;
+        m_delayFailTimer.startOneShot(randomDelay());
+    } else
+        send(Messages::WebSocketStream::DidFailSocketStream(error));
 }
 
 IPC::Connection* NetworkSocketStream::messageSenderConnection() const

Modified: trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.h (264305 => 264306)


--- trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.h	2020-07-13 17:36:21 UTC (rev 264305)
+++ trunk/Source/WebKit/NetworkProcess/NetworkSocketStream.h	2020-07-13 17:57:59 UTC (rev 264306)
@@ -28,8 +28,10 @@
 #include "MessageReceiver.h"
 #include "MessageSender.h"
 #include "WebSocketIdentifier.h"
+#include <WebCore/SocketStreamError.h>
 #include <WebCore/SocketStreamHandleClient.h>
 #include <WebCore/SocketStreamHandleImpl.h>
+#include <WebCore/Timer.h>
 #include <pal/SessionID.h>
 
 namespace IPC {
@@ -62,6 +64,7 @@
     void didFailSocketStream(WebCore::SocketStreamHandle&, const WebCore::SocketStreamError&) final;
 
 private:
+    void sendDelayedFailMessage();
     IPC::Connection* messageSenderConnection() const final;
     uint64_t messageSenderDestinationID() const final;
 
@@ -70,6 +73,8 @@
     WebSocketIdentifier m_identifier;
     IPC::Connection& m_connection;
     Ref<WebCore::SocketStreamHandleImpl> m_impl;
+    WebCore::Timer m_delayFailTimer;
+    WebCore::SocketStreamError m_closedPortError;
 };
 
 } // namespace WebKit
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to