Issue #3465 - internal WebSocket extensions

do not allow internal extensions to be offered by the client
do not validate internal extensions

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2019-03-21 12:05:18 +11:00
parent 26e7881dbd
commit eb4f7c000f
7 changed files with 50 additions and 10 deletions

View File

@ -282,6 +282,9 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon
List<ExtensionConfig> offeredExtensions = getExtensions();
for (ExtensionConfig config : extensions)
{
if (config.getName().startsWith("@"))
continue;
long numMatch = offeredExtensions.stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
if (numMatch < 1)
throw new WebSocketException("Upgrade failed: Sec-WebSocket-Extensions contained extension not requested");

View File

@ -130,7 +130,7 @@ public class Negotiation
?Collections.emptyList()
:extensions.getValues().stream()
.map(ExtensionConfig::parse)
.filter(ec -> available.contains(ec.getName().toLowerCase()))
.filter(ec -> available.contains(ec.getName().toLowerCase()) && !ec.getName().startsWith("@"))
.collect(Collectors.toList());
offeredSubprotocols = subprotocols == null

View File

@ -170,6 +170,9 @@ public final class RFC6455Handshaker implements Handshaker
// validate negotiated extensions
for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
{
if (config.getName().startsWith("@"))
continue;
long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
if (matches < 1)
throw new WebSocketException("Upgrade failed: negotiated extension not requested");

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.core;
import java.io.IOException;
import java.util.List;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.DecoratedObjectFactory;
@ -44,7 +43,7 @@ public class TestWebSocketNegotiator implements WebSocketNegotiator
}
public TestWebSocketNegotiator(DecoratedObjectFactory objectFactory, WebSocketExtensionRegistry extensionRegistry, ByteBufferPool bufferPool,
FrameHandler frameHandler)
FrameHandler frameHandler)
{
this.objectFactory = objectFactory;
this.extensionRegistry = extensionRegistry;
@ -56,13 +55,9 @@ public class TestWebSocketNegotiator implements WebSocketNegotiator
public FrameHandler negotiate(Negotiation negotiation) throws IOException
{
List<String> offeredSubprotocols = negotiation.getOfferedSubprotocols();
if (!offeredSubprotocols.contains("test"))
return null;
negotiation.setSubprotocol("test");
if (!offeredSubprotocols.isEmpty())
negotiation.setSubprotocol(offeredSubprotocols.get(0));
// TODO better to call negotiation.setNegotiatedExtensions();
negotiation.getResponse().addHeader(HttpHeader.SEC_WEBSOCKET_EXTENSIONS.asString(),
"@validation; outgoing-sequence; incoming-sequence; outgoing-frame; incoming-frame; incoming-utf8; outgoing-utf8");
return frameHandler;
}

View File

@ -1,3 +1,21 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.core;
import java.io.IOException;

View File

@ -18,17 +18,23 @@
package org.eclipse.jetty.websocket.core.extensions;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.ExtensionConfig;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.core.RawFrameBuilder;
import org.eclipse.jetty.websocket.core.TestFrameHandler;
import org.eclipse.jetty.websocket.core.TestWebSocketNegotiator;
import org.eclipse.jetty.websocket.core.WebSocketServer;
import org.eclipse.jetty.websocket.core.WebSocketTester;
import org.eclipse.jetty.websocket.core.server.Negotiation;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -48,7 +54,19 @@ public class ValidationExtensionTest extends WebSocketTester
public void start() throws Exception
{
serverHandler = new TestFrameHandler();
WebSocketNegotiator negotiator = new TestWebSocketNegotiator(serverHandler);
WebSocketNegotiator negotiator = new TestWebSocketNegotiator(serverHandler)
{
@Override
public FrameHandler negotiate(Negotiation negotiation) throws IOException
{
List<ExtensionConfig> negotiatedExtensions = new ArrayList<>();
negotiatedExtensions.add(ExtensionConfig.parse(
"@validation; outgoing-sequence; incoming-sequence; outgoing-frame; incoming-frame; incoming-utf8; outgoing-utf8"));
negotiation.setNegotiatedExtensions(negotiatedExtensions);
return super.negotiate(negotiation);
}
};
server = new WebSocketServer(negotiator);
server.start();
}

View File

@ -170,6 +170,9 @@ public class ServletUpgradeResponse
// This validation is also done later in RFC6455Handshaker but it is better to fail earlier
for (ExtensionConfig config : configs)
{
if (config.getName().startsWith("@"))
continue;
long matches = negotiation.getOfferedExtensions().stream().filter(e -> e.getName().equals(config.getName())).count();
if (matches < 1)
throw new IllegalArgumentException("Extension not a requested extension");