Christian Schudt  committed 72e062f

Protect unsecure BOSH sessions by using the key sequence mechanism

  • Participants
  • Parent commits 70c0a7d
  • Branches server

Comments (0)

Files changed (2)

File xmpp-sample/src/main/java/rocks/xmpp/sample/SampleApplication.java Modified

View file
  • Ignore whitespace
  • Hide word diff
                         .port(5280)
                         //.sslContext(getTrustAllSslContext())
                         .secure(false)
+                        .useKeySequence(true)
                         .compressionMethods(CompressionMethod.GZIP, CompressionMethod.DEFLATE)
                         .build();
                 WebSocketConnectionConfiguration webSocketConfiguration = WebSocketConnectionConfiguration.builder()
                         .debugger(ConsoleDebugger.class)
                         .build();
 
-                XmppClient xmppClient = XmppClient.create("localhost", configuration, tcpConfiguration);
+                XmppClient xmppClient = XmppClient.create("localhost", configuration, boshConfiguration);
 
                 // Listen for inbound messages.
                 xmppClient.addInboundMessageListener(e -> logger.info("Received: " + e.getMessage()));

File xmpp-server/src/main/java/rocks/xmpp/extensions/httpbind/server/BoshConnection.java Modified

View file
  • Ignore whitespace
  • Hide word diff
 import rocks.xmpp.extensions.httpbind.model.Body;
 import rocks.xmpp.server.Connection;
 import rocks.xmpp.server.ServerConfiguration;
+import rocks.xmpp.util.XmppUtils;
 
 import javax.enterprise.inject.spi.CDI;
 import javax.ws.rs.container.AsyncResponse;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
      * The highest received RID. All lower RIDs have also been received.
      * Guarded by "inboundQueue".
      */
-    private long lastRid = -1;
+    private Body lastRequest;
 
     private Instant lastResponseDate;
 
                 // Check the RID of the next request.
                 while ((queuedRequest = inboundQueue.peek()) != null) {
                     // Loop while the next element's RID is ok.
-                    if (lastRid < 0 || queuedRequest.body.getRid() == lastRid + 1) {
-                        orderedRequests.add(inboundQueue.poll());
-                        lastRid = body.getRid();
+                    if (lastRequest == null || queuedRequest.body.getRid() == lastRequest.getRid() + 1) {
+                        final BodyRequest nextRequest = inboundQueue.poll();
+                        // Verify the keys, if the requests are using the key sequencing mechanism.
+                        if (!verifyKey(nextRequest.body, lastRequest)) {
+                            condition = Body.Condition.ITEM_NOT_FOUND;
+                            break;
+                        } else {
+                            orderedRequests.add(nextRequest);
+                        }
+                        lastRequest = body;
                     } else {
                         // We received a RID which is out of order.
                         // Do nothing with the request and wait if the next request contains the missing RID,
                         // unless the RID is larger than the upper limit of the expected window
-                        if (Math.abs(queuedRequest.body.getRid() - lastRid) > simultaneousRequests) {
+                        if (Math.abs(queuedRequest.body.getRid() - lastRequest.getRid()) > simultaneousRequests) {
                             condition = Body.Condition.ITEM_NOT_FOUND;
+                            break;
                         }
                         break;
                     }
         }
     }
 
+    /**
+     * Verifies a request by comparing the hash of its key with the previous request's key.
+     *
+     * @param body            The request.
+     * @param previousRequest The previous request.
+     * @return true, if verified.
+     * @see <a href="https://xmpp.org/extensions/xep-0124.html#keys">15. Protecting Insecure Sessions</a>
+     */
+    private static boolean verifyKey(final Body body, final Body previousRequest) {
+        if (previousRequest != null && (previousRequest.getNewKey() != null || previousRequest.getKey() != null)) {
+            if (body.getKey() == null) {
+                // If it receives a request without a 'key' attribute and the 'newkey' or 'key' attribute of the previous request was set
+                // then the connection manager MUST NOT process the element
+                return false;
+            } else {
+                final String hashedKey = XmppUtils.hash(body.getKey().getBytes(StandardCharsets.UTF_8));
+                final String previousKey = previousRequest.getNewKey() != null ? previousRequest.getNewKey() : previousRequest.getKey();
+                return hashedKey.equalsIgnoreCase(previousKey);
+            }
+        }
+        // No previous request or previous request is not using the key sequencing mechanism.
+        return true;
+    }
+
     @Override
     public final void close(final StreamError streamError) {
         deliverables.add(streamError);
             final Body bodyToSend;
 
             synchronized (inboundQueue) {
-                if (lastRid != body.getRid()) {
-                    response.ack(lastRid);
+                if (!lastRequest.getRid().equals(body.getRid())) {
+                    response.ack(lastRequest.getRid());
                 }
             }
             bodyToSend = response.build();