# BungeeGuard A **hardening of [legacy forwarding](bungeecord-legacy.md)**, not a new transport. Legacy's fatal flaw is that anyone who can reach the backend port can forge the `\0`-delimited identity string and connect as anyone. BungeeGuard fixes that by smuggling a **shared secret token** into the forwarded data; the backend plugin refuses the login unless the token matches. It bridges backends that **only support legacy forwarding** up to "can't be trivially forged," without requiring [modern/Velocity](velocity-modern.md) support. It is **third-party** — not part of Spigot/Paper core or the Mojang protocol. The original is the [BungeeGuard plugin](https://github.com/lucko/BungeeGuard) (lucko), installed on both the proxy and every backend. ## How it works BungeeGuard reuses the exact legacy wire format — the four `\0`-separated handshake-address segments (`realHost\0clientIP\0uuidNoDashes\0propertiesJSON`). The trick is in the **properties JSON**: it appends one **synthetic game-profile property** named **`bungeeguard-token`** whose value is the shared secret. A game-profile property is a `{name, value, signature?}` object — the same shape as the Mojang `textures` property. BungeeGuard injects an extra one: ```json [ { "name": "textures", "value": "...", "signature": "..." }, { "name": "bungeeguard-token","value": "" } ] ``` On the backend, the BungeeGuard plugin (replacing the stock legacy parser) reads the player's properties, finds the `bungeeguard-token` entry, and compares its value against the secret it was configured with. Match → accept and strip the token; mismatch or absent → reject the connection. The real IP/UUID/skin are still taken from the same legacy segments. Velocity has this mode built in (`player-info-forwarding-mode = "legacy"` is bare legacy; `"bungeeguard"` adds the token). Its source builds the address identically to legacy and just adds the token property: > ```java > private static final String BUNGEE_GUARD_TOKEN_PROPERTY_NAME = "bungeeguard-token"; > ... > final GameProfile.Property property = new GameProfile.Property( > BUNGEE_GUARD_TOKEN_PROPERTY_NAME, > new String(forwardingSecret, StandardCharsets.UTF_8), > ""); > return createLegacyForwardingAddress(serverAddress, playerAddress, profile, > properties -> ImmutableList.builder() > .addAll(properties).add(property).build()); > ``` > > — `Velocity/proxy/src/main/java/com/velocitypowered/proxy/connection/PlayerDataForwarding.java:52` (name) + `:175-196` (`createBungeeGuardForwardingAddress`) So a BungeeGuard handshake address is exactly a legacy one with one extra property in the JSON array. ## Security: better than legacy, weaker than modern | | bare legacy | BungeeGuard | modern (Velocity) | |---|---|---|---| | Forge-able by anyone reaching the port | **Yes** | No (needs the token) | No (needs the secret) | | Secret/token crosses the wire | n/a | **Yes — token sent in cleartext** inside the connection | No (only an HMAC crosses) | | Tamper-proofs IP/UUID/props | No | **No** — only gate-keeps; the identity fields are still unsigned plaintext | **Yes** — whole payload is HMAC'd | | Backend requirement | core `bungeecord:true` | BungeeGuard plugin | Paper/Fabric modern support | The crucial limitation: BungeeGuard is **authentication of the connection, not authentication of the payload**. It proves "whoever sent this knows the token," but the IP/UUID/properties themselves are not signed — once the token check passes, those fields are trusted as-is, exactly like legacy. And the token is transmitted **in cleartext** in the handshake. So: - If the connection between proxy and backend is **not** encrypted/private, a network observer can lift the token and then forge freely. - The token is a single shared value across all backends; leaking it from any one host compromises all. It is a real improvement over bare legacy (you no longer rely solely on a firewall) and the right choice for a backend that **cannot** do modern forwarding. But when every backend supports it, **[modern/Velocity forwarding](velocity-modern.md) is strictly stronger** — it signs the payload with an HMAC and never puts the secret on the wire. ## When to use it - A backend (old Spigot, a plugin platform, a Fabric server without modern-forward support) only speaks **legacy**, and you can't firewall it tightly enough to trust bare legacy. - You're migrating a BungeeCord/Waterfall network and want hardening without switching every backend to Velocity-native forwarding. Otherwise prefer modern. See the [README comparison table](README.md#comparison). --- **Sources** - `Velocity/proxy/src/main/java/com/velocitypowered/proxy/connection/PlayerDataForwarding.java:52` (`bungeeguard-token` property name), `:175-196` (`createBungeeGuardForwardingAddress` — legacy address + token property), `:154-173` (shared `createLegacyForwardingAddress` it builds on). - [lucko/BungeeGuard](https://github.com/lucko/BungeeGuard) — the original third-party plugin (token property + backend check). - See [bungeecord-legacy.md](bungeecord-legacy.md) for the underlying `\0`-delimited wire format BungeeGuard extends.