You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
Merge pull request #217 from simoleo89/fix/nitro-secure-asset-safety
fix(auth): bound secure asset file reads
This commit is contained in:
+13
@@ -35,6 +35,8 @@ public class NitroSecureAssetHandler extends ChannelInboundHandlerAdapter {
|
|||||||
private static final String BOOTSTRAP_PATH = "/nitro-sec/bootstrap";
|
private static final String BOOTSTRAP_PATH = "/nitro-sec/bootstrap";
|
||||||
private static final String FILE_PATH = "/nitro-sec/file";
|
private static final String FILE_PATH = "/nitro-sec/file";
|
||||||
private static final int MAX_BOOTSTRAP_BODY_BYTES = 4096;
|
private static final int MAX_BOOTSTRAP_BODY_BYTES = 4096;
|
||||||
|
private static final int DEFAULT_MAX_CONFIG_BYTES = 2 * 1024 * 1024;
|
||||||
|
private static final int DEFAULT_MAX_GAMEDATA_BYTES = 16 * 1024 * 1024;
|
||||||
private static final SecureRandom RNG = new SecureRandom();
|
private static final SecureRandom RNG = new SecureRandom();
|
||||||
private static final KeyPair SERVER_KEYPAIR = createServerKeyPair();
|
private static final KeyPair SERVER_KEYPAIR = createServerKeyPair();
|
||||||
private static final String SERVER_KEY_FINGERPRINT = fingerprint(SERVER_KEYPAIR.getPublic().getEncoded());
|
private static final String SERVER_KEY_FINGERPRINT = fingerprint(SERVER_KEYPAIR.getPublic().getEncoded());
|
||||||
@@ -146,6 +148,9 @@ public class NitroSecureAssetHandler extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
if (!target.startsWith(root)) throw new IllegalArgumentException("Invalid file.");
|
if (!target.startsWith(root)) throw new IllegalArgumentException("Invalid file.");
|
||||||
if (!Files.isRegularFile(target)) throw new IOException("Not found");
|
if (!Files.isRegularFile(target)) throw new IOException("Not found");
|
||||||
|
long size = Files.size(target);
|
||||||
|
int maxBytes = maxAssetBytes(kind);
|
||||||
|
if (size > maxBytes) throw new IllegalArgumentException("File too large.");
|
||||||
|
|
||||||
String cacheKey = kind + ":" + target;
|
String cacheKey = kind + ":" + target;
|
||||||
long modified = Files.getLastModifiedTime(target).toMillis();
|
long modified = Files.getLastModifiedTime(target).toMillis();
|
||||||
@@ -158,6 +163,14 @@ public class NitroSecureAssetHandler extends ChannelInboundHandlerAdapter {
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int maxAssetBytes(String kind) {
|
||||||
|
boolean config = "config".equals(kind);
|
||||||
|
String key = config ? "nitro.secure.config.max_file_bytes" : "nitro.secure.gamedata.max_file_bytes";
|
||||||
|
int fallback = config ? DEFAULT_MAX_CONFIG_BYTES : DEFAULT_MAX_GAMEDATA_BYTES;
|
||||||
|
int configured = Emulator.getConfig().getInt(key, fallback);
|
||||||
|
return configured > 0 ? configured : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
private static String normalizeFile(String file) {
|
private static String normalizeFile(String file) {
|
||||||
if (file == null) throw new IllegalArgumentException("Missing file.");
|
if (file == null) throw new IllegalArgumentException("Missing file.");
|
||||||
String value = URLDecoder.decode(file, StandardCharsets.UTF_8).replace('\\', '/');
|
String value = URLDecoder.decode(file, StandardCharsets.UTF_8).replace('\\', '/');
|
||||||
|
|||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package com.eu.habbo.networking.gameserver.auth;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class NitroSecureAssetHandlerContractTest {
|
||||||
|
private static String handlerSource() throws Exception {
|
||||||
|
return Files.readString(Path.of("src/main/java/com/eu/habbo/networking/gameserver/auth/NitroSecureAssetHandler.java"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String emulatorSource() throws Exception {
|
||||||
|
return Files.readString(Path.of("src/main/java/com/eu/habbo/Emulator.java"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void secureAssetFilesAreSizeCheckedBeforeReadAndCache() throws Exception {
|
||||||
|
String handler = handlerSource();
|
||||||
|
String emulator = emulatorSource();
|
||||||
|
|
||||||
|
int size = handler.indexOf("long size = Files.size(target)");
|
||||||
|
int maxBytes = handler.indexOf("int maxBytes = maxAssetBytes(kind)", size);
|
||||||
|
int oversizedGuard = handler.indexOf("size > maxBytes", maxBytes);
|
||||||
|
int cacheLookup = handler.indexOf("CACHE.get(cacheKey)", oversizedGuard);
|
||||||
|
int readAllBytes = handler.indexOf("Files.readAllBytes(target)", oversizedGuard);
|
||||||
|
|
||||||
|
assertTrue(handler.contains("DEFAULT_MAX_CONFIG_BYTES = 2 * 1024 * 1024"),
|
||||||
|
"Secure config assets should have a conservative default file cap");
|
||||||
|
assertTrue(handler.contains("DEFAULT_MAX_GAMEDATA_BYTES = 16 * 1024 * 1024"),
|
||||||
|
"Secure gamedata assets should have a bounded default file cap");
|
||||||
|
assertTrue(handler.contains("nitro.secure.config.max_file_bytes"),
|
||||||
|
"Secure config max file size should be configurable");
|
||||||
|
assertTrue(handler.contains("nitro.secure.gamedata.max_file_bytes"),
|
||||||
|
"Secure gamedata max file size should be configurable");
|
||||||
|
assertTrue(size > -1, "Secure assets must inspect file size before loading bytes");
|
||||||
|
assertTrue(maxBytes > size, "Secure assets must resolve the configured cap before loading bytes");
|
||||||
|
assertTrue(oversizedGuard > maxBytes, "Secure assets must reject oversized files");
|
||||||
|
assertTrue(oversizedGuard < cacheLookup, "Oversized secure assets must not be served from cache");
|
||||||
|
assertTrue(oversizedGuard < readAllBytes, "Oversized secure assets must be rejected before readAllBytes");
|
||||||
|
assertTrue(emulator.contains("register(\"nitro.secure.config.max_file_bytes\", \"2097152\")"),
|
||||||
|
"Secure config max file size default must be registered before startup");
|
||||||
|
assertTrue(emulator.contains("register(\"nitro.secure.gamedata.max_file_bytes\", \"16777216\")"),
|
||||||
|
"Secure gamedata max file size default must be registered before startup");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user