Skip to content
cccskills
FreePublished on GitHub

game-security-pitfalls

Use when reviewing game code in C#, GDScript or C++ (Unity, Godot, Unreal, custom engines) - client vs server authority, RPC and packet validation, in-app purchases and economy, save and replay files, mod and script loading, secrets in client builds

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md7.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Game Security Pitfalls

Overview

Everything in the client — memory, files, packets — belongs to the player. That makes most single-player cheating a non-finding (attacker and victim are the same person) and makes everything that affects other players or real money a real one: multiplayer outcomes, leaderboards, trading, purchases, shared content.

Core principle: clients send intent ("move north", "fire toward x"), the server decides outcomes ("hit for 37"). Any message that carries an outcome is a finding when other players depend on it.

1. Server Authority (CWE-602)

// ❌ Unity Netcode for GameObjects: any client can call it, and it reports the result
[ServerRpc(RequireOwnership = false)]
void ApplyDamageServerRpc(ulong targetId, int damage) { players[targetId].Health -= damage; }

// ✅ the client sends intent; the server checks the sender and computes the outcome
[Rpc(SendTo.Server)]
void FireRpc(Vector3 direction, RpcParams rpcParams = default) {
    var shooter = PlayerFor(rpcParams.Receive.SenderClientId);
    if (!shooter.CanFire()) return;
    weapons.ResolveShot(shooter, direction.normalized);
}
# ❌ Godot: any peer may call it and claim any score
@rpc("any_peer")
func submit_score(player_id: int, score: int) -> void:
    scores[player_id] = score

# ✅ identity from the transport, outcome from server state
@rpc("any_peer", "reliable")
func request_finish_level() -> void:
    var id := multiplayer.get_remote_sender_id()
    scores[id] = compute_score(id)
// ❌ Unreal: the validation function accepts everything
bool AMyCharacter::ServerSetHealth_Validate(float NewHealth) { return true; }
// ✅ no client-settable health; server RPCs carry inputs and validate ranges
bool AMyCharacter::ServerUseItem_Validate(int32 Slot) { return Slot >= 0 && Slot < Inventory.Num(); }

Also: Mirror [Command(requiresAuthority = false)] acting on objects the caller does not own; a client-authoritative transform with no server-side speed or teleport check in a competitive mode.

2. Network Message Validation

Every field in a packet is attacker-chosen.

  • Sizes and indices: a length or count read from the packet used to allocate, copy or index. In C/C++ this is memory corruption (CWE-120, CWE-787) — in scope, since these are not memory-safe languages (native-memory-safety-and-ffi for bounds, integer overflow and lifetimes).
  • Ranges: item slots, entity ids, enum values, positions; NaN and Infinity floats passing </> checks.
  • Ownership: the entity id in the message belongs to the sender.
  • Versioning: an unknown message type or version is rejected, not interpreted with the newest layout.
// ❌ count comes from the wire
uint16_t count = reader.ReadU16();
for (int i = 0; i < count; ++i) items[i] = reader.ReadU32();   // items holds 32
// ✅
uint16_t count = reader.ReadU16();
if (count > std::size(items) || reader.Remaining() < count * 4) return Disconnect(peer);

3. Economy and Purchases

  • Currency, items or entitlements granted from a client message, or from an in-app purchase receipt validated only on the device → free items for everyone with a proxy. Receipts are verified server-side with the store, once, and recorded to stop replay.
  • Trades and crafting resolved client-side; duplicate-item races on the server where the same item can be spent twice (this is integrity, not a theoretical race — report it when two requests can both pass the check).

4. Hidden Information

Sending state the player should not see — other players' hands, fog-of-war positions, upcoming loot — to every client lets any modified client reveal it. In competitive modes it is a finding (MEDIUM to HIGH); filter what the server replicates per player.

5. Save Files, Replays and Shared Content (CWE-502)

Tampering with a local single-player save is not a finding. Deserializing a file that someone else can supply is:

  • C# BinaryFormatter (also NetDataContractSerializer, LosFormatter) on save files, replays or downloaded content → code execution; use System.Text.Json, MessagePack with known types, or a custom schema.
  • Godot: FileAccess.get_var(true), bytes_to_var_with_objects, and loading .tres/.res/.tscn from untrusted sources can instantiate objects with embedded scripts; use get_var() with objects disallowed, or JSON.
  • Saves uploaded to cloud slots, leaderboards or PvP snapshots are server input — validate them like any request.

6. Mods, Scripts and User-Generated Content

  • Loading assemblies or native libraries from a mods directory (Assembly.LoadFrom, dlopen), Lua with os/io/load available, GDScript or other engine scripts from downloaded content: full code execution on the player's machine. A finding when the game fetches or auto-installs that content from untrusted sources, or advertises it as sandboxed when it is not.
  • A server that loads player-uploaded scripts, maps or replays without sandboxing → CRITICAL.
  • Downloaded content paths (zip-slip in mod archives — path-traversal-and-file-handling).

7. Secrets in Client Builds

Back-end admin or title secret keys (live-ops platforms, publisher web API keys, analytics write secrets with admin scope, dedicated-server credentials) compiled into the client are leaked to every player. Public client ids are fine.

Not Findings

Single-player cheats, memory editing of the player's own game, speed-ups in offline modes, missing anti-cheat or obfuscation, a client that can see data it legitimately needs to render.

Common Mistakes

  • Reporting a single-player save edit as a vulnerability.
  • Reporting C# or GDScript memory safety — memory-safe; C++ packet parsing is the exception.
  • Accepting a _Validate function or a RequireOwnership flag as proof the server checks the outcome.

Red Flags

  • RPCs or messages named Set…, Add…, Grant…, SubmitScore… callable by clients.
  • RequireOwnership = false, requiresAuthority = false, @rpc("any_peer") without a sender check.
  • _Validate returning true unconditionally.
  • BinaryFormatter, get_var(true), Assembly.LoadFrom on files other people supply.
  • Purchase grants with no server-side receipt verification.

References (names and links only)

Unity Netcode for GameObjects · Godot high-level multiplayer · Unreal RPCs · gafferongames networking · BinaryFormatter security guide · CWE-120, 502, 602, 787

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

The English summary is being prepared. Showing the original description.

makifbaysal/tasktrooper117Updated Oct 21, 2026

See all skills by makifbaysal

Report a problem with this skill