What can anyone holding the App ID and JavaScript key from your frontend do to your data, and how do you stop them? Twelve requests from four identities against one class, run with the class open and again after each of four locks: class-level permissions, an owner-only ACL stamped by a backend hook, a role checked in a Cloud Function, and a master key that never leaves the server. Built for a Back4app backend (managed Parse Server); the scripts are bash and curl.
Measured on September 17, 2026: an open class allowed 12 of 12 requests, including an anonymous DELETE of another user's row. After the locks, 6 of 12: the owner's own rows and the master key. Every table in the article comes from these scripts.
Read the article: Your Frontend Talks Straight to the Database. Here Is How to Lock It Down
| Identity → request | Open class | CLP + ACL + role |
|---|---|---|
| anonymous (JS key) → list, create, delete | 200 / 201 / 200 | 404 / 101 |
| ana (session token) → read, update her note | 200 / 200 | 200 / 200 |
| bob (session token) → read, update, delete ana's note | 200 / 200 / 200 | 404 / 101 |
| master key → read, delete ana's note | 200 / 200 | 200 / 200 |
moderatorList Cloud Function |
— | ana → 119 · bob (moderator) → every row |
The ACL hides a row it denies: bob gets the same 404 / 101 Object not found as a made-up id, not "permission denied".
probe.sh— the twelve requests: anonymous with the JavaScript key,anaandbobwith session tokens, and the master key. Prints HTTP status, Parse error code and thex-backend-serverthat answered.seed.sh— createsanaandboband amoderatorrole containingbob.lockdown.sh open|authenticated|show— lock 1 as a script: the class-level permissions ofNotethrough the schema API. The dashboard's Database → class menu → Security → Class Level Permission sets the same thing by hand.cloud/main.js— locks 2 and 3, deployed as Cloud Code: abeforeSave("Note")that stamps an owner-only ACL on every new row, and amoderatorListCloud Function that only members of the role can call. Lock 4 is theuseMasterKey: trueinside it, and nowhere else.migrate-acl.sh— rows created before the hook keep their old ACL. This fixes owned rows and deletes ownerless ones.
- After changing class-level permissions, the app answered inconsistently for 22 minutes: some servers applied the new rule, one (
x-backend-servershowed which) kept the old one. Restart App did not end it; a Cloud Code deploy did. Probe until you get 0 of N, not until the first denial. - A class created by a client write is born with Public Read + Write on every row and an open CLP,
addFieldincluded. The lock is opt-in, every time. - With
requiresAuthentication, a deniedfind/getis404 / 101and a deniedcountis400 / 119. Branch on both.
- Create a free backend. Sign up at https://www.back4app.com/signup?utm_source=github&utm_medium=repo&utm_campaign=lockdown-lab, then New App → Build your Backend. The free plan is enough for everything here.
- App Settings → Security & Keys: copy the App ID, the JavaScript key and the Master key into
.env(git-ignored;.env.exampleshows the names). The master key stays on your machine. - Run the scripts below.
seed.shcreates the users and the role; the firstprobe.shis the open-class table. - Cloud Code → main.js: paste
cloud/main.jsand click Deploy, twice on a fresh backend (the first deploy ships nothing). Prove it: a logged-in create must show the owner's id in the ACL column of Database → Note. - Watch the class in Database → Note: the ACL column changes from Public Read + Write to the owner's id as the hook takes over, and Security → Class Level Permission shows lock 1.
cp .env.example .env # APP_ID, JS_KEY, MASTER_KEY
set -a; . ./.env; set +a
./seed.sh # ana, bob, moderator role
./probe.sh # state 0: everything open
./lockdown.sh authenticated && sleep 300 && ./probe.sh # state 1: class-level permissions (re-run until 0 of N)
# deploy cloud/main.js in the dashboard (twice on a fresh backend), then:
./probe.sh # state 2: owner ACLs + role
./migrate-acl.sh # rows from before the hookA managed Parse Server with a database, REST and GraphQL APIs, Cloud Code, and the three permission layers this repo exercises: class-level permissions, per-object ACLs and roles. Documentation: https://www.back4app.com/docs?utm_source=github&utm_medium=repo&utm_campaign=lockdown-lab · security guide: https://www.back4app.com/docs/security/parse-server-security.
If the app does not have accounts yet, start with the companion repo user-auth-starter.
MIT