mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/LCE-Revelations.git
synced 2026-06-01 01:45:11 +00:00
feat: dedicated server security hardening
Comprehensive security system to protect against packet-sniffing attacks, XUID harvesting, privilege escalation, bot flooding, and XUID impersonation. - Stream cipher: per-session XOR cipher with 4-message handshake via CustomPayloadPacket (MC|CKey, MC|CAck, MC|COn). Negotiated per-connection, backwards compatible (old clients/servers fall back to plaintext). - Security gate: buffers all game data until cipher handshake completes, preventing unsecured clients from receiving any XUIDs or game state. - Cipher handshake enforcer: kicks clients that don't complete the handshake within 5 seconds (configurable via require-secure-client). - Identity tokens: persistent per-XUID tokens in identity-tokens.json, issued over the encrypted channel, verified on reconnect. Prevents XUID replay attacks. Client stores server-specific tokens. - PROXY protocol v1: parses real client IPs from playit.gg tunnel headers so rate limiting, IP bans, and XUID spoof detection work per-player. - Rate limiting: per-IP sliding window (default 5 connections/30s) with pending connection cap (default 10). - Privilege hardening: OP requires ops.json, live checks on every command and privilege packet. Host-only server settings changes. - XUID stripping: PreLoginPacket response sends INVALID_XUID placeholders. - Packet validation: readUtf global string cap, reduced max packet size, stream desync protection on oversized strings. - OpManager: persistent ops.json with XUID-based OP list. - Whitelist improvements: whitelist add accepts player names with ambiguity detection, XUID cache from login attempts. - revoketoken command: revoke identity tokens for players who lost theirs. - server.log: persistent log file written alongside console output with flush-per-write to survive crashes. - CLI security logging: consolidated per-join security summary with cipher status, token status, XUID, and real IP. Security warnings for kicks, spoofing, and unauthorized commands.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
#include "..\Minecraft.Server\ServerLogManager.h"
|
||||
#include "..\Minecraft.Server\Access\Access.h"
|
||||
#include "..\Minecraft.Server\Security\SecurityConfig.h"
|
||||
#include "..\Minecraft.World\Socket.h"
|
||||
#endif
|
||||
// #ifdef __PS3__
|
||||
@@ -150,6 +151,20 @@ void PendingConnection::sendPreLoginResponse()
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Security: strip real XUIDs from pre-login response to prevent unauthenticated enumeration.
|
||||
// The client receives the correct player count but cannot identify who is connected.
|
||||
// Real XUID data is sent post-login via PlayerInfoPacket broadcasts.
|
||||
if (ServerRuntime::Security::GetSettings().hidePlayerListPreLogin)
|
||||
{
|
||||
for (DWORD i = 0; i < ugcXuidCount; ++i)
|
||||
{
|
||||
ugcXuids[i] = INVALID_XUID;
|
||||
}
|
||||
ugcFriendsOnlyBits = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (false)// server->onlineMode) // 4J - removed
|
||||
{
|
||||
@@ -203,6 +218,56 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
||||
duplicateXuid = true;
|
||||
}
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Cross-reference: if someone claims the same XUID as an existing player from a different IP,
|
||||
// log and reject as a potential spoofing attempt.
|
||||
// Note: this runs on the main tick thread (via PendingConnection::tick -> Connection::tick ->
|
||||
// handleLogin), same thread that mutates the player list, so no lock is needed.
|
||||
if (!duplicateXuid && loginXuid != INVALID_XUID)
|
||||
{
|
||||
std::string newIp;
|
||||
unsigned char newSmallId = GetPendingConnectionSmallId(connection);
|
||||
bool hasNewIp = ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(newSmallId, &newIp);
|
||||
|
||||
for (auto &existingPlayer : server->getPlayers()->players)
|
||||
{
|
||||
if (existingPlayer == nullptr) continue;
|
||||
PlayerUID existingXuid = existingPlayer->connection->m_offlineXUID;
|
||||
if (existingXuid == INVALID_XUID) existingXuid = existingPlayer->connection->m_onlineXUID;
|
||||
if (existingXuid == loginXuid)
|
||||
{
|
||||
if (hasNewIp)
|
||||
{
|
||||
std::string existingIp;
|
||||
INetworkPlayer *np = existingPlayer->connection->getNetworkPlayer();
|
||||
if (np != nullptr)
|
||||
{
|
||||
unsigned char existingSmallId = np->GetSmallId();
|
||||
if (ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(existingSmallId, &existingIp))
|
||||
{
|
||||
if (existingIp != newIp)
|
||||
{
|
||||
app.DebugPrintf("SECURITY: XUID spoofing suspected - XUID 0x%016llx claimed from IP %s while already connected from IP %s\n",
|
||||
(unsigned long long)loginXuid, newIp.c_str(), existingIp.c_str());
|
||||
ServerRuntime::ServerLogManager::OnXuidSpoofDetected(newSmallId, name, newIp.c_str(), existingIp.c_str());
|
||||
duplicateXuid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot verify IP -- treat same-XUID connection as suspicious
|
||||
app.DebugPrintf("SECURITY: XUID 0x%016llx claimed but could not verify source IP\n",
|
||||
(unsigned long long)loginXuid);
|
||||
duplicateXuid = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bannedXuid = false;
|
||||
if (loginXuid != INVALID_XUID)
|
||||
{
|
||||
@@ -243,7 +308,11 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
|
||||
else if (!whitelistSatisfied)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Cache name->XUID so `whitelist add <name>` can resolve the XUID
|
||||
ServerRuntime::ServerLogManager::CachePlayerXuid(name, loginXuid);
|
||||
ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted);
|
||||
app.DebugPrintf("WHITELIST: Rejected %ls (XUID: 0x%016llx) - use 'whitelist add %ls' to allow\n",
|
||||
name.c_str(), (unsigned long long)loginXuid, name.c_str());
|
||||
#endif
|
||||
disconnect(DisconnectPacket::eDisconnect_Banned);
|
||||
}
|
||||
@@ -330,11 +399,17 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
|
||||
PlayerUID playerXuid = packet->m_offlineXuid;
|
||||
if(playerXuid == INVALID_XUID) playerXuid = packet->m_onlineXuid;
|
||||
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
// Cache name->XUID for console commands (whitelist add, revoketoken, etc.)
|
||||
ServerRuntime::ServerLogManager::CachePlayerXuid(name, playerXuid);
|
||||
#endif
|
||||
|
||||
shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid);
|
||||
if (playerEntity != nullptr)
|
||||
{
|
||||
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
|
||||
ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
|
||||
ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name,
|
||||
packet->m_offlineXuid, packet->m_onlineXuid, packet->m_isGuest);
|
||||
#endif
|
||||
server->getPlayers()->placeNewPlayer(connection, playerEntity, packet);
|
||||
connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor
|
||||
|
||||
Reference in New Issue
Block a user