Skip to content

Commit

Permalink
fix: use time-constant comparison for security tokens (#12192)
Browse files Browse the repository at this point in the history
This is the same as #12190, but also applied for the upload security key
and the push id since both of those are also used to protect against
cross-site attacks. In addition, documentation for the push id is
clarified to point out its role.

Backporting of #12189
  • Loading branch information
TatuLund authored Feb 3, 2021
1 parent 46ecb27 commit d0d2cfb
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 6 deletions.
4 changes: 4 additions & 0 deletions server/src/main/java/com/vaadin/server/VaadinSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,10 @@ public Collection<UI> getUIs() {

private final String csrfToken = UUID.randomUUID().toString();

/*
* This token should be handled with care since it's used to protect against
* cross-site attacks in addition to general identifier duty.
*/
private final String pushId = UUID.randomUUID().toString();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.MessageDigest;

import com.vaadin.server.ClientConnector;
import com.vaadin.server.NoInputStreamException;
Expand Down Expand Up @@ -273,8 +274,10 @@ public boolean handleRequest(VaadinSession session, VaadinRequest request,
streamVariable = uI.getConnectorTracker()
.getStreamVariable(connectorId, variableName);
String secKey = uI.getConnectorTracker().getSeckey(streamVariable);
if (secKey == null || !secKey.equals(parts[3])) {
// TODO Should rethink error handling
String securityKey = parts[3];
if (secKey == null || !MessageDigest.isEqual(
secKey.getBytes(UTF8),
securityKey.getBytes(UTF8))) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.IOException;
import java.io.Reader;
import java.security.MessageDigest;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -57,6 +58,8 @@ public class PushHandler {

private int longPollingSuspendTimeout = -1;

private static final String UTF8 = "UTF-8";

/**
* Callback interface used internally to process an event with the
* corresponding UI properly locked.
Expand Down Expand Up @@ -471,7 +474,9 @@ private static final Logger getLogger() {
}

/**
* Checks whether a given push id matches the session's push id.
* Checks whether a given push id matches the session's push id. The
* comparison is done using a time-constant method since the push id is used
* to protect against cross-site attacks.
*
* @param session
* the vaadin session for which the check should be done
Expand All @@ -480,10 +485,12 @@ private static final Logger getLogger() {
* @return {@code true} if the id is valid, {@code false} otherwise
*/
private static boolean isPushIdValid(VaadinSession session,
String requestPushId) {
String requestPushId) throws IOException {

String sessionPushId = session.getPushId();
if (requestPushId == null || !requestPushId.equals(sessionPushId)) {
if (requestPushId == null || !MessageDigest.isEqual(
requestPushId.getBytes(UTF8),
sessionPushId.getBytes(UTF8))) {
return false;
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void verifyUserAgent() {
// Chrome version does not necessarily match the desired version
// because of auto updates...
browserIdentifier = getExpectedUserAgentString(
getDesiredCapabilities()) + "87";
getDesiredCapabilities()) + "88";
} else {
browserIdentifier = getExpectedUserAgentString(desiredCapabilities)
+ desiredCapabilities.getVersion();
Expand Down

0 comments on commit d0d2cfb

Please sign in to comment.