mirror of
https://github.com/google/nomulus.git
synced 2025-05-30 01:10:14 +02:00
Remove XsrfTokenManager fallback for validating legacy tokens
This is the third step of migrating to the new format: removing support for the legacy format. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150512556
This commit is contained in:
parent
09f619cce2
commit
232132eac0
2 changed files with 18 additions and 54 deletions
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
package google.registry.security;
|
package google.registry.security;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.io.BaseEncoding.base64Url;
|
import static com.google.common.io.BaseEncoding.base64Url;
|
||||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
@ -23,7 +22,6 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import google.registry.model.server.ServerSecret;
|
import google.registry.model.server.ServerSecret;
|
||||||
import google.registry.util.Clock;
|
import google.registry.util.Clock;
|
||||||
|
@ -45,9 +43,6 @@ public final class XsrfTokenManager {
|
||||||
/** Token version identifier for version 1. */
|
/** Token version identifier for version 1. */
|
||||||
private static final String VERSION_1 = "1";
|
private static final String VERSION_1 = "1";
|
||||||
|
|
||||||
/** Legacy scope values that will be supported during the scope removal process. */
|
|
||||||
private static final ImmutableSet<String> LEGACY_SCOPES = ImmutableSet.of("admin", "console");
|
|
||||||
|
|
||||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||||
|
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
@ -82,36 +77,18 @@ public final class XsrfTokenManager {
|
||||||
return Joiner.on(':').join(VERSION_1, timestampMillis, hmac);
|
return Joiner.on(':').join(VERSION_1, timestampMillis, hmac);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Validates an XSRF token against the current logged-in user. */
|
||||||
* Computes the hash payload portion of a legacy-style XSRF token.
|
|
||||||
*
|
|
||||||
* <p>The result is a Base64-encoded SHA-256 hash of a string containing the secret, email, scope
|
|
||||||
* and creation time, separated by tabs.
|
|
||||||
*/
|
|
||||||
private static String computeLegacyHash(long creationTime, String scope, String userEmail) {
|
|
||||||
checkArgument(LEGACY_SCOPES.contains(scope), "Invalid scope value: %s", scope);
|
|
||||||
String token =
|
|
||||||
Joiner.on('\t').join(ServerSecret.get().asUuid(), userEmail, scope, creationTime);
|
|
||||||
return base64Url().encode(Hashing.sha256()
|
|
||||||
.newHasher(token.length())
|
|
||||||
.putString(token, UTF_8)
|
|
||||||
.hash()
|
|
||||||
.asBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates an XSRF token against the current logged-in user.
|
|
||||||
*
|
|
||||||
* This accepts both legacy-style and new-style XSRF tokens. For legacy-style tokens, it will
|
|
||||||
* accept tokens generated with any scope from {@link #LEGACY_SCOPES}.
|
|
||||||
*/
|
|
||||||
public boolean validateToken(String token) {
|
public boolean validateToken(String token) {
|
||||||
checkArgumentNotNull(token);
|
checkArgumentNotNull(token);
|
||||||
List<String> tokenParts = Splitter.on(':').splitToList(token);
|
List<String> tokenParts = Splitter.on(':').splitToList(token);
|
||||||
if (tokenParts.size() < 2) {
|
if (tokenParts.size() != 3) {
|
||||||
logger.warningfmt("Malformed XSRF token: %s", token);
|
logger.warningfmt("Malformed XSRF token: %s", token);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!tokenParts.get(0).equals(VERSION_1)) {
|
||||||
|
logger.warningfmt("Unrecognized version in XSRF token: %s", token);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
String timePart = tokenParts.get(1);
|
String timePart = tokenParts.get(1);
|
||||||
long timestampMillis;
|
long timestampMillis;
|
||||||
try {
|
try {
|
||||||
|
@ -127,8 +104,7 @@ public final class XsrfTokenManager {
|
||||||
String currentUserEmail =
|
String currentUserEmail =
|
||||||
userService.isUserLoggedIn() ? userService.getCurrentUser().getEmail() : "";
|
userService.isUserLoggedIn() ? userService.getCurrentUser().getEmail() : "";
|
||||||
|
|
||||||
// Reconstruct the token to verify validity, using version 1 format if detected.
|
// Reconstruct the token to verify validity.
|
||||||
if (tokenParts.get(0).equals(VERSION_1)) {
|
|
||||||
String reconstructedToken =
|
String reconstructedToken =
|
||||||
encodeToken(ServerSecret.get().asBytes(), currentUserEmail, timestampMillis);
|
encodeToken(ServerSecret.get().asBytes(), currentUserEmail, timestampMillis);
|
||||||
if (!token.equals(reconstructedToken)) {
|
if (!token.equals(reconstructedToken)) {
|
||||||
|
@ -137,22 +113,5 @@ public final class XsrfTokenManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
// TODO(b/35388772): remove this fallback once we no longer generate legacy tokens.
|
|
||||||
// Fall back to the legacy format, and try the few possible scopes.
|
|
||||||
String hash = tokenParts.get(0);
|
|
||||||
ImmutableSet.Builder<String> reconstructedTokenCandidates = new ImmutableSet.Builder<>();
|
|
||||||
for (String scope : LEGACY_SCOPES) {
|
|
||||||
String reconstructedHash = computeLegacyHash(timestampMillis, scope, currentUserEmail);
|
|
||||||
reconstructedTokenCandidates.add(reconstructedHash);
|
|
||||||
if (hash.equals(reconstructedHash)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.warningfmt(
|
|
||||||
"Reconstructed XSRF mismatch: %s matches none of %s",
|
|
||||||
hash, reconstructedTokenCandidates.build());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,12 @@ public class XsrfTokenManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidate_tokenWithMissingParts() {
|
public void testValidate_tokenWithMissingParts() {
|
||||||
assertThat(xsrfTokenManager.validateToken("foo")).isFalse();
|
assertThat(xsrfTokenManager.validateToken("1:123")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidate_tokenWithBadVersion() {
|
||||||
|
assertThat(xsrfTokenManager.validateToken("2:123:base64")).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue