Leaderboard Cheating: Validate Scores Server-Side
Stop fake leaderboard scores by separating score authority from ranking storage. Covers trusted submission, replay validation, bounds, seasons, and recovery.
The short version
- Authentication is not score validation. A legitimate player token can still submit a dishonest number if your API accepts client-authored scores.
- Ranking storage should not decide whether gameplay was legitimate. Compute or validate competitive results in trusted game logic, then submit the accepted result.
- Bounds catch nonsense, not cheating. A maximum score can reject
999999999; it cannot prove that a plausible48,210came from legal play. - Recovery is a product decision. Freeze or retire a compromised season, fix the authority boundary, and publish a clean season rather than pretending old ranks are trustworthy.
A public leaderboard is not inherently insecure. The insecure part is letting an untrusted process choose the competitive result and treating that number as truth. If a modified client can say “my score is 9000000”, HTTPS, API-key obfuscation and player login do not make the score honest. They only tell you which authenticated client made the claim.
The clean architecture is two separate responsibilities: trusted game logic decides the result; the leaderboard service stores, updates and ranks that result. That distinction matters whether your game uses an authoritative dedicated server, a listen host plus backend validation, deterministic replay verification, or a single-player run checked asynchronously after completion.
What actually has to be trusted?
Do not start with “how do I secure the POST request?” Start with “which process has enough evidence to decide this score?” The answer depends on the game:
| Game shape | Useful authority | Leaderboard submission |
|---|---|---|
| Authoritative multiplayer match | The game server already owns kills, objectives, time and the final result. | The trusted server computes the score and submits it after the match. |
| Deterministic / replayable run | A verifier can replay inputs or event evidence against the same rules. | Accept the score only after replay/event verification succeeds. |
| Single-player, weakly verifiable | No trusted process observed the whole run. | Treat the board as unverified, or use sanity checks and accept that plausible cheating remains possible. |
| Server-backed progression metric | The backend already owns the durable event: win, purchase, quest completion, etc. | Derive the leaderboard delta from that trusted event instead of accepting a second client claim. |
This is why “server-side leaderboard” is not enough. A cloud endpoint that blindly accepts {score: 48210} is still client-authoritative if the client chose 48,210.
Authentication prevents impersonation, not fabrication
Player authentication is still important. It prevents one player from writing under another player's identity, gives moderation actions a stable target, and lets the service enforce tenancy. But it solves a different question: who is calling? Competitive validation asks: is the result true?
For Crux specifically, the normal score endpoint accepts either a player token or a server token. With a player token, player_id must match the authenticated player. With a server token, trusted infrastructure may submit for the player whose result it just computed. That is a useful credential boundary, but Crux does not inspect your match simulation and cannot know whether the score itself was earned legitimately.
Three validation levels
1. Server-computed result
This is the strongest normal pattern. The authoritative simulation or trusted result processor owns the underlying events, computes the final number and sends it to the ranking service. The client can display the number, but it never gets to author the competitive write.
// Pseudocode inside trusted match-result processing
result = validate_match_and_compute_result(match)
if result.valid:
leaderboard.submit_server_side(
player_id = result.player_id,
score = result.score,
metadata = { "match_id": result.match_id }
)
Notice what is missing: there is no “client says score, server forwards score” step.
2. Replay or evidence validation
If you cannot keep a live authoritative server for every run, submit evidence rather than only a final scalar. Depending on the game, that can be deterministic inputs, a compact event log, checkpoints, a signed session record, or another representation your verifier can recompute. Validation is only as strong as the evidence: replaying a client-authored event log without checking game rules just moves the trust problem into a larger payload.
3. Sanity checks and score bounds
Bounds are useful defense-in-depth. Reject non-finite values, impossible negative values, impossible per-run maxima, implausible rates, or submissions outside a known game-mode envelope. They cheaply remove obvious garbage. They do not prove legal play inside the permitted range.
Crux leaderboards support optional min/max checks on each submitted value. Those checks are deliberately simple storage-policy guards. They are not an anti-cheat engine.
Make retries safe before you make cheating hard
Competitive integrity and delivery correctness are separate failure modes. A trusted game server can still retry after a timeout. Decide what a duplicate means before launch:
bestkeeps the better submitted score for the board's sort order. Re-sending the same result is naturally harmless to the stored score.replacestores the latest accepted value. Use it when the value itself is a snapshot, not an additive event.sumadds each accepted submission to the player's total. This is useful for cumulative wins/points, but duplicate delivery would also add twice. If one match must count once, deduplicate by your own stable match/event id before submitting the delta.
That last distinction is important: “server-token submission” protects who may write; it does not automatically give every game event exactly-once semantics.
What to do with an already-poisoned board
Do not silently invent forensic certainty you do not have. If the old authority model accepted arbitrary client values, a plausible-looking historical score may be indistinguishable from a legitimate one.
- Stop the unsafe write path first. Otherwise every cleanup is temporary.
- Preserve evidence before destructive cleanup. Keep raw records/logs you may need to diagnose how submissions were made.
- Classify only what you can prove. Impossible values can be rejected confidently; “suspiciously good” is not the same thing as proven cheating.
- Start a clean competitive season when trust is uncertain. Label the old season accordingly instead of presenting compromised history as verified.
- Separate verified and open competition if both are useful. Two boards with honest labels are better than one board with ambiguous integrity.
Crux keeps leaderboard seasons, so a new season is a natural recovery boundary. It does not currently expose a public “delete arbitrary leaderboard entry” API; do not design your incident response around a moderation endpoint that does not exist.
Metadata helps investigation; it does not make the score trusted
A stable match id, game mode, build version and validation version are useful metadata. They let you answer questions such as “did every impossible score come from build 1.4.2?” or “did this match result get processed twice?” But metadata sent by an untrusted client is still untrusted. Attach competitive metadata in the same trusted result-processing path that submits the score.
Leaderboard integrity is narrower than anti-cheat
A trustworthy board does not require you to solve every cheat. Wallhacks, information leakage, aim assistance and botting may still require simulation authority, telemetry, platform anti-cheat or human review. The leaderboard problem is narrower: do not turn an untrusted final number directly into competitive state.
For a multiplayer title, the broader trust model is covered in server-authoritative anti-cheat and authoritative server design. For the ranking mechanics themselves, see the leaderboard kata.
Where Crux fits: and where it does not
Crux supplies the ranking layer: player identity, server-token and player-token score submission, top/standing/around-player reads, seasons, ascending or descending rank order, optional per-submission score bounds, and best, replace and sum update strategies.
Crux does not currently watch your gameplay, replay matches, detect impossible movement, prove that a run was legitimate, deduplicate your match ids, or turn a client-authored score into a server-authoritative result. Competitive games should perform that validation in trusted game/server logic and use Crux to store and rank the accepted outcome.
The one-line rule: authenticate the player, validate the game result, then rank it. Do not ask the leaderboard database to perform all three jobs.