Skip to content

Commit

Permalink
Improve protection against spam attacks
Browse files Browse the repository at this point in the history
  • Loading branch information
nickuc committed Aug 13, 2024
1 parent f3f8261 commit d362a77
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface BungeeGuardBackend {
Path getConfigPath();

void reloadConfig();

boolean isVerbose();
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import me.lucko.bungeeguard.backend.BungeeGuardBackend;
import me.lucko.bungeeguard.backend.TokenStore;

import java.util.concurrent.atomic.AtomicLong;

/**
* An abstract handshake listener.
*/
Expand All @@ -38,7 +40,7 @@ public abstract class AbstractHandshakeListener {
protected final String noDataKickMessage;
protected final String invalidTokenKickMessage;

private long throttle;
private final AtomicLong throttle = new AtomicLong();

protected AbstractHandshakeListener(BungeeGuardBackend plugin, TokenStore tokenStore) {
this.plugin = plugin;
Expand All @@ -47,13 +49,8 @@ protected AbstractHandshakeListener(BungeeGuardBackend plugin, TokenStore tokenS
this.invalidTokenKickMessage = plugin.getKickMessage("invalid-token-kick-message");
}

public boolean isThrottled() {
long cur = System.currentTimeMillis();
if (cur - this.throttle >= 1000L) {
this.throttle = cur;
return false;
} else {
return true;
}
public boolean isRateLimitAllowed() {
long current = System.currentTimeMillis();
return current - this.throttle.getAndSet(current) >= 1000;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ private static BungeeCordHandshake decodeAndVerify0(String handshake, TokenStore
readIndex = 1;
}
String socketAddressHostname = split[readIndex++];
UUID uniqueId = UUID.fromString(split[readIndex++].replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
UUID uniqueId;
try {
uniqueId = UUID.fromString(split[readIndex++].replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
} catch (NumberFormatException e) {
return new Fail(Fail.Reason.INVALID_UNIQUE_ID, encodeBase64(handshake));
}

String connectionDescription = uniqueId + " @ " + encodeBase64(socketAddressHostname);

Expand All @@ -108,7 +113,7 @@ private static BungeeCordHandshake decodeAndVerify0(String handshake, TokenStore
JsonObject property = iterator.next();
if (property.get(PROPERTY_NAME_KEY).getAsString().equals(BUNGEEGUARD_TOKEN_NAME)) {
if (bungeeGuardToken != null) {
return new Fail(Fail.Reason.INCORRECT_TOKEN, connectionDescription + " - more than one token");
return new Fail(Fail.Reason.DUPLICATED_TOKEN, connectionDescription);
}

bungeeGuardToken = property.get(PROPERTY_VALUE_KEY).getAsString();
Expand Down Expand Up @@ -195,8 +200,7 @@ public String describeConnection() {
}

public enum Reason {
INVALID_HANDSHAKE, NO_TOKEN, INCORRECT_TOKEN
INVALID_HANDSHAKE, INVALID_UNIQUE_ID, NO_TOKEN, DUPLICATED_TOKEN, INCORRECT_TOKEN
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ public Path getConfigPath() {
return new File(getDataFolder(), "config.yml").toPath();
}

@Override
public boolean isVerbose() {
return getConfig().getBoolean("verbose", false);
}

private boolean isBungeeEnabled() {
YamlConfiguration spigotConfig = getServer().spigot().getSpigotConfig();
return spigotConfig.getBoolean("settings.bungeecord") || spigotConfig.getBoolean("spigot.settings.bungeecord");
Expand Down Expand Up @@ -173,5 +178,4 @@ private static boolean classExists(String className) {
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
public class PaperHandshakeListener extends AbstractHandshakeListener implements Listener {

private static final Method getOriginalSocketAddressHostname;

static {
Method method = null;
try {
Expand All @@ -69,17 +70,17 @@ public void onHandshake(PlayerHandshakeEvent e) {
BungeeCordHandshake.Fail fail = (BungeeCordHandshake.Fail) decoded;

// if the logging is not throttled, we send the error message
if (!isThrottled()) {
String ip = "";
if (isRateLimitAllowed()) {
String ip = "null";
if (getOriginalSocketAddressHostname != null) {
try {
ip = getOriginalSocketAddressHostname.invoke(e) + " - ";
ip = (String) getOriginalSocketAddressHostname.invoke(e);
} catch (ReflectiveOperationException ex) {
this.logger.log(Level.SEVERE, "Unable to get original address", ex);
}
}

this.logger.warning("Denying connection from " + ip + fail.describeConnection() + " - reason: " + fail.reason().name());
this.logger.warning("Denying connection from " + ip + " - " + (plugin.isVerbose() ? fail.describeConnection() : "") + " - reason: " + fail.reason().name());
}

if (fail.reason() == BungeeCordHandshake.Fail.Reason.INCORRECT_TOKEN) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@ public ProtocolHandshakeListener(BungeeGuardBackend plugin, TokenStore tokenStor
}

public void registerAdapter(Plugin plugin) {
ProtocolLibrary.getProtocolManager().addPacketListener(new Adapter(plugin));
ProtocolLibrary.getProtocolManager().addPacketListener(new Adapter(plugin, super.plugin));
}

private final class Adapter extends PacketAdapter {
Adapter(Plugin plugin) {

private final BungeeGuardBackend backend;

Adapter(Plugin plugin, BungeeGuardBackend backend) {
super(plugin, ListenerPriority.LOWEST, PacketType.Handshake.Client.SET_PROTOCOL);
this.backend = backend;
}

@Override
Expand All @@ -80,7 +84,7 @@ public void onPacketReceiving(PacketEvent event) {
Player player = event.getPlayer();

// if the logging is not throttled, we send the error message
if (!isThrottled()) {
if (isRateLimitAllowed()) {
String ip = "null";
InetSocketAddress address = player.getAddress();
if (address != null) {
Expand All @@ -89,7 +93,7 @@ public void onPacketReceiving(PacketEvent event) {
ip = BungeeCordHandshake.encodeBase64(ip);
}
}
this.plugin.getLogger().warning("Denying connection from " + ip + " - " + fail.describeConnection() + " - reason: " + fail.reason().name());
this.plugin.getLogger().warning("Denying connection from " + ip + " - " + (backend.isVerbose() ? fail.describeConnection() : "") + " - reason: " + fail.reason().name());
}

String kickMessage;
Expand Down
6 changes: 5 additions & 1 deletion bungeeguard-spigot/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ invalid-token-kick-message:
- '&cThe token sent by proxy server is not allowed by this backend server.'
- ''
- '§7More information:'
- '&bdocs.nickuc.com/bungeeguard'
- '&bdocs.nickuc.com/bungeeguard'

# Defines if BungeeGuard should detail errors in the console.
# Useful for debugging purposes.
verbose: false
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,9 @@ public void reloadConfig() {
throw new RuntimeException("Unable to load config", e);
}
}

@Override
public boolean isVerbose() {
return this.config.getNode("verbose").getBoolean(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void onClientAuth(ClientConnectionEvent.Auth e) {

if (bungeeGuardToken == null || !this.tokenStore.isAllowed(bungeeGuardToken)) {
// if the logging is not throttled, we send the error message
if (!isThrottled()) {
if (isRateLimitAllowed()) {
String connectionDescription = profile.getUniqueId() + " @ " + e.getConnection().getAddress().getHostString();
String reason = bungeeGuardToken == null ? "No Token" : "Invalid token";

Expand Down
4 changes: 4 additions & 0 deletions bungeeguard-sponge/src/main/resources/bungeeguard.conf
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ invalid-token-kick-message = [
"§7More information:",
"&bdocs.nickuc.com/bungeeguard",
]

# Defines if BungeeGuard should detail errors in the console.
# Useful for debugging purposes.
verbose = false

0 comments on commit d362a77

Please sign in to comment.