Use UUIDs as rate limiter keys.

This commit is contained in:
Jon Chambers
2021-06-30 16:31:21 -04:00
committed by Jon Chambers
parent becf6afbdd
commit a680639718
20 changed files with 98 additions and 89 deletions

View File

@@ -207,17 +207,17 @@ public class AccountController {
throw new WebApplicationException(Response.status(400).build());
}
String requester = ForwardedIpUtil.getMostRecentProxy(forwardedFor).orElseThrow();
String sourceHost = ForwardedIpUtil.getMostRecentProxy(forwardedFor).orElseThrow();
Optional<StoredVerificationCode> storedChallenge = pendingAccounts.getCodeForNumber(number);
CaptchaRequirement requirement = requiresCaptcha(number, transport, forwardedFor, requester, captcha, storedChallenge, pushChallenge);
CaptchaRequirement requirement = requiresCaptcha(number, transport, forwardedFor, sourceHost, captcha, storedChallenge, pushChallenge);
if (requirement.isCaptchaRequired()) {
captchaRequiredMeter.mark();
if (requirement.isAutoBlock() && shouldAutoBlock(requester)) {
logger.info("Auto-block: " + requester);
abusiveHostRules.setBlockedHost(requester, "Auto-Block");
if (requirement.isAutoBlock() && shouldAutoBlock(sourceHost)) {
logger.info("Auto-block: {}", sourceHost);
abusiveHostRules.setBlockedHost(sourceHost, "Auto-Block");
}
return Response.status(402).build();
@@ -405,7 +405,7 @@ public class AccountController {
@Path("/turn/")
@Produces(MediaType.APPLICATION_JSON)
public TurnToken getTurnToken(@Auth Account account) throws RateLimitExceededException {
rateLimiters.getTurnLimiter().validate(account.getNumber());
rateLimiters.getTurnLimiter().validate(account.getUuid());
return turnTokenGenerator.generate();
}
@@ -568,7 +568,7 @@ public class AccountController {
@Path("/username/{username}")
@Produces(MediaType.APPLICATION_JSON)
public Response setUsername(@Auth Account account, @PathParam("username") String username) throws RateLimitExceededException {
rateLimiters.getUsernameSetLimiter().validate(account.getUuid().toString());
rateLimiters.getUsernameSetLimiter().validate(account.getUuid());
if (username == null || username.isEmpty()) {
return Response.status(Response.Status.BAD_REQUEST).build();
@@ -588,14 +588,14 @@ public class AccountController {
}
private CaptchaRequirement requiresCaptcha(String number, String transport, String forwardedFor,
String requester,
String sourceHost,
Optional<String> captchaToken,
Optional<StoredVerificationCode> storedVerificationCode,
Optional<String> pushChallenge)
{
if (captchaToken.isPresent()) {
boolean validToken = recaptchaClient.verify(captchaToken.get(), requester);
boolean validToken = recaptchaClient.verify(captchaToken.get(), sourceHost);
if (validToken) {
captchaSuccessMeter.mark();
@@ -633,18 +633,18 @@ public class AccountController {
}
}
List<AbusiveHostRule> abuseRules = abusiveHostRules.getAbusiveHostRulesFor(requester);
List<AbusiveHostRule> abuseRules = abusiveHostRules.getAbusiveHostRulesFor(sourceHost);
for (AbusiveHostRule abuseRule : abuseRules) {
if (abuseRule.isBlocked()) {
logger.info("Blocked host: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
logger.info("Blocked host: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
blockedHostMeter.mark();
return new CaptchaRequirement(true, false);
}
if (!abuseRule.getRegions().isEmpty()) {
if (abuseRule.getRegions().stream().noneMatch(number::startsWith)) {
logger.info("Restricted host: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
logger.info("Restricted host: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
filteredHostMeter.mark();
return new CaptchaRequirement(true, false);
}
@@ -652,9 +652,9 @@ public class AccountController {
}
try {
rateLimiters.getSmsVoiceIpLimiter().validate(requester);
rateLimiters.getSmsVoiceIpLimiter().validate(sourceHost);
} catch (RateLimitExceededException e) {
logger.info("Rate limited exceeded: " + transport + ", " + number + ", " + requester + " (" + forwardedFor + ")");
logger.info("Rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
rateLimitedHostMeter.mark();
return new CaptchaRequirement(true, true);
}
@@ -662,7 +662,7 @@ public class AccountController {
try {
rateLimiters.getSmsVoicePrefixLimiter().validate(Util.getNumberPrefix(number));
} catch (RateLimitExceededException e) {
logger.info("Prefix rate limit exceeded: " + transport + ", " + number + ", (" + forwardedFor + ")");
logger.info("Prefix rate limit exceeded: {}, {}, {} ({})", transport, number, sourceHost, forwardedFor);
rateLimitedPrefixMeter.mark();
return new CaptchaRequirement(true, true);
}
@@ -682,9 +682,9 @@ public class AccountController {
accounts.delete(account, AccountsManager.DeletionReason.USER_REQUEST);
}
private boolean shouldAutoBlock(String requester) {
private boolean shouldAutoBlock(String sourceHost) {
try {
rateLimiters.getAutoBlockLimiter().validate(requester);
rateLimiters.getAutoBlockLimiter().validate(sourceHost);
} catch (RateLimitExceededException e) {
return true;
}

View File

@@ -49,7 +49,7 @@ public class AttachmentControllerV1 extends AttachmentControllerBase {
throws RateLimitExceededException
{
if (account.isRateLimited()) {
rateLimiters.getAttachmentLimiter().validate(account.getNumber());
rateLimiters.getAttachmentLimiter().validate(account.getUuid());
}
long attachmentId = generateAttachmentId();

View File

@@ -41,7 +41,7 @@ public class AttachmentControllerV2 extends AttachmentControllerBase {
@Produces(MediaType.APPLICATION_JSON)
@Path("/form/upload")
public AttachmentDescriptorV2 getAttachmentUploadForm(@Auth Account account) throws RateLimitExceededException {
rateLimiter.validate(account.getNumber());
rateLimiter.validate(account.getUuid());
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
long attachmentId = generateAttachmentId();

View File

@@ -58,7 +58,7 @@ public class AttachmentControllerV3 extends AttachmentControllerBase {
@Produces(MediaType.APPLICATION_JSON)
@Path("/form/upload")
public AttachmentDescriptorV3 getAttachmentUploadForm(@Auth Account account) throws RateLimitExceededException {
rateLimiter.validate(account.getNumber());
rateLimiter.validate(account.getUuid());
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final String key = generateAttachmentKey();

View File

@@ -112,7 +112,7 @@ public class DeviceController {
public VerificationCode createDeviceToken(@Auth Account account)
throws RateLimitExceededException, DeviceLimitExceededException
{
rateLimiters.getAllocateDeviceLimiter().validate(account.getNumber());
rateLimiters.getAllocateDeviceLimiter().validate(account.getUuid());
int maxDeviceLimit = MAX_DEVICES;

View File

@@ -40,11 +40,9 @@ import org.whispersystems.textsecuregcm.limits.PreKeyRateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeException;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.sqs.DirectoryQueue;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.storage.KeysDynamoDb;
import org.whispersystems.textsecuregcm.util.Util;
@@ -57,7 +55,6 @@ public class KeysController {
private final AccountsManager accounts;
private final PreKeyRateLimiter preKeyRateLimiter;
private final DynamicConfigurationManager dynamicConfigurationManager;
private final RateLimitChallengeManager rateLimitChallengeManager;
private static final String PREKEY_REQUEST_COUNTER_NAME = name(KeysController.class, "preKeyGet");
@@ -69,15 +66,12 @@ public class KeysController {
public KeysController(RateLimiters rateLimiters, KeysDynamoDb keysDynamoDb, AccountsManager accounts,
PreKeyRateLimiter preKeyRateLimiter,
DynamicConfigurationManager dynamicConfigurationManager,
RateLimitChallengeManager rateLimitChallengeManager) {
this.rateLimiters = rateLimiters;
this.keysDynamoDb = keysDynamoDb;
this.accounts = accounts;
this.preKeyRateLimiter = preKeyRateLimiter;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.rateLimitChallengeManager = rateLimitChallengeManager;
this.rateLimitChallengeManager = rateLimitChallengeManager;
}
@GET
@@ -152,7 +146,7 @@ public class KeysController {
}
if (account.isPresent()) {
rateLimiters.getPreKeysLimiter().validate(account.get().getNumber() + "." + account.get().getAuthenticatedDevice().get().getId() + "__" + target.get().getNumber() + "." + deviceId);
rateLimiters.getPreKeysLimiter().validate(account.get().getUuid() + "." + account.get().getAuthenticatedDevice().get().getId() + "__" + target.get().getUuid() + "." + deviceId);
try {
preKeyRateLimiter.validate(account.get());

View File

@@ -257,7 +257,7 @@ public class MessageController {
assert(destination.isPresent());
if (source.isPresent() && !source.get().isFor(destinationName)) {
rateLimiters.getMessagesLimiter().validate(source.get().getNumber() + "__" + destination.get().getUuid());
rateLimiters.getMessagesLimiter().validate(source.get().getUuid(), destination.get().getUuid());
final String senderCountryCode = Util.getCountryCode(source.get().getNumber());

View File

@@ -210,7 +210,7 @@ public class ProfileController {
}
if (requestAccount.isPresent()) {
rateLimiters.getProfileLimiter().validate(requestAccount.get().getNumber());
rateLimiters.getProfileLimiter().validate(requestAccount.get().getUuid());
}
Optional<Account> accountProfile = accountsManager.get(uuid);
@@ -260,7 +260,7 @@ public class ProfileController {
@Produces(MediaType.APPLICATION_JSON)
@Path("/username/{username}")
public Profile getProfileByUsername(@Auth Account account, @PathParam("username") String username) throws RateLimitExceededException {
rateLimiters.getUsernameLookupLimiter().validate(account.getUuid().toString());
rateLimiters.getUsernameLookupLimiter().validate(account.getUuid());
username = username.toLowerCase();
@@ -341,7 +341,7 @@ public class ProfileController {
}
if (requestAccount.isPresent()) {
rateLimiters.getProfileLimiter().validate(requestAccount.get().getNumber());
rateLimiters.getProfileLimiter().validate(requestAccount.get().getUuid());
}
Optional<Account> accountProfile = accountsManager.get(identifier);

View File

@@ -6,13 +6,8 @@
package org.whispersystems.textsecuregcm.controllers;
import com.codahale.metrics.annotation.Timed;
import org.whispersystems.textsecuregcm.entities.ProvisioningMessage;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.websocket.InvalidWebsocketAddressException;
import org.whispersystems.textsecuregcm.websocket.ProvisioningAddress;
import io.dropwizard.auth.Auth;
import java.util.Base64;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
@@ -22,10 +17,11 @@ import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Base64;
import io.dropwizard.auth.Auth;
import org.whispersystems.textsecuregcm.entities.ProvisioningMessage;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.push.ProvisioningManager;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.websocket.ProvisioningAddress;
@Path("/v1/provisioning")
public class ProvisioningController {
@@ -46,9 +42,9 @@ public class ProvisioningController {
public void sendProvisioningMessage(@Auth Account source,
@PathParam("destination") String destinationName,
@Valid ProvisioningMessage message)
throws RateLimitExceededException, InvalidWebsocketAddressException, IOException
{
rateLimiters.getMessagesLimiter().validate(source.getNumber());
throws RateLimitExceededException {
rateLimiters.getMessagesLimiter().validate(source.getUuid());
if (!provisioningManager.sendProvisioningMessage(new ProvisioningAddress(destinationName, 0),
Base64.getDecoder().decode(message.getBody())))

View File

@@ -49,7 +49,7 @@ public class StickerController {
@PathParam("count") @Min(1) @Max(201) int stickerCount)
throws RateLimitExceededException
{
rateLimiters.getStickerPackLimiter().validate(account.getNumber());
rateLimiters.getStickerPackLimiter().validate(account.getUuid());
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
String packId = generatePackId();