Skip to content

[Security] Authenticated users can forge leaderboard entries with arbitrary XP via direct INSERT/DELETE #1925

Description

@atul-upadhyay-7

Summary

The leaderboard table still grants INSERT and DELETE to authenticated users. A client can delete their own row and re-insert it with arbitrary xp, streak, sessions_joined, and badges values — bypassing the whole gamification security model.

Evidence

supabase/migrations/20260617000000_consolidate_rls_policies.sql:

CREATE POLICY "Users can insert leaderboard entry"
  ON public.leaderboard FOR INSERT WITH CHECK (user_id = auth.uid());

CREATE POLICY "Users can delete leaderboard entry"
  ON public.leaderboard FOR DELETE USING (user_id = auth.uid());

The recent hardening (20260730000001_secure_leaderboard_updates.sql, 20260803000002_enforce_leaderboard_score_immutability.sql) only covers UPDATE. INSERT and DELETE are unguarded.

Exploit

-- delete own row, then re-insert with a fabricated top score
delete from public.leaderboard where user_id = auth.uid();
insert into public.leaderboard (user_id, username, xp, streak, sessions_joined, badges)
values (auth.uid(), 'x', 2147483647, 9999, 999, '{top}');

This instantly puts the attacker at rank #1 in every leaderboard view and awards them top badges. Repeatable at any time.

Impact

  • Leaderboard integrity completely broken; fake scores and badges.
  • Undermines the anti-forgery guarantees documented in the 08-03 migration.

Suggested Fix

  1. Drop both INSERT and DELETE policies for the leaderboard table and REVOKE INSERT, DELETE ON public.leaderboard FROM anon, authenticated;.
  2. Keep the existing SECURITY DEFINER join_leaderboard RPC as the only legitimate row-creation path (it already zero-initializes the score fields).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions