Your app's client keys are inside your web and mobile apps, so anyone can find them and call your API as your app does. That's expected: Parse is built so the keys only identify the app, and permissions decide what each caller may do. This page is about setting them so that a stranger with your keys can do nothing they shouldn't.
The model in one table
| Layer | Decides | Where |
|---|---|---|
| Class-level permissions (CLPs) | Who may find, get, create, update, delete in a class, and add fields | Core › Browser › class › Security |
| ACLs | Who may read and write one object | The object's ACL field, usually set in code |
| Roles | Groups of users, used in CLPs and ACLs | The _Role class |
| Cloud code | Anything that needs more than one user's rights | Your app's repository, cloud/ |
| Master key | Bypasses all of the above | Server side only |
A request has to pass the CLP first, then each object's ACL. On Parse Server 6, a class's CLP can also hide fields from some users (protectedFields, set through the schema API), e.g. other users' emails.
Lock down the classes
New classes are open to everyone by default. For each class, decide who needs what:
- Find lists objects. Most classes don't need it public, and
_Userespecially shouldn't have it. - Create, Update, Delete: only for authenticated users or a role, not "Public".
- Add field: off for everyone. Add fields from the dashboard or your code with the master key.
Then turn off Allow client class creation (App Settings › Security & Keys), so clients can't create new classes by saving objects.
Give objects an owner
An ACL says who may read and write one object. Set it when the object is created, in a beforeSave trigger, so clients can't forget or forge it:
// cloud/main.js
Parse.Cloud.beforeSave('Note', (request) => {
if (!request.original && request.user) {
const acl = new Parse.ACL(request.user); // the author reads and writes
acl.setRoleReadAccess('moderators', true); // moderators read
request.object.setACL(acl);
}
});
Keep privileged logic on the server
Anything that changes data the user doesn't own (payments, credits, counters, roles, other users' records) belongs in a cloud function that checks the caller and then acts with the master key:
Parse.Cloud.define('grantCredits', async (request) => {
const { user } = request;
if (!user) throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Log in first.');
const isAdmin = await new Parse.Query(Parse.Role)
.equalTo('name', 'admins').equalTo('users', user).first({ useMasterKey: true });
if (!isAdmin) throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Admins only.');
// ...the change, with { useMasterKey: true }
});
Never trust a field the client sends for decisions like isAdmin or price: read them from the database in the function.
Keep keys and secrets where they belong
- Master Key, REST API Key, Webhook Key, Database URL: server side only. Never in an app, a repository, a support ticket or a prompt. If one leaks, reset it (App Settings › Security & Keys › Reset master key).
- Third-party secrets (Stripe, email services): in environment variables, read in cloud code from
process.env, never in the code itself. - Scripts, CI and AI assistants: a personal access token with only the scopes they need, not the master key. You can revoke it, limit it to apps, and see everything it did in Account › Activity.
Check it
- App Checks in the dashboard (Parse Server 6.5.11 and newer) runs security, users and database checks against your app and says how to fix each finding.
- Through MCP, *"check my-app for security problems"* runs
get_app_advice, including the class-level permission checks on older versions. - Turn on two-factor authentication for your SashiDo account (Account › Security), and review Account › Activity for changes you didn't make.
Checklist before launch
- Every class has CLPs for its purpose; nothing public that doesn't need to be
-
_UserFind is not public - Allow client class creation is off
- Objects get ACLs in
beforeSave - Privileged changes are in cloud functions that check the caller
- No master key or secret in client code or repositories; secrets in environment variables
- App Checks (or
get_app_advice) show no failures - Two-factor authentication on for everyone with dashboard access