Base44 app data exposed: how to check and lock it down
A Base44 app exposes data when an entity has no row-level security (RLS) rule, and the fix is to add a rule for each operation on each entity, push the rules, and test with a second account. The Base44 docs state that if no RLS is defined, all records are accessible to all users. The app screen does not change this.
This guide shows how to confirm the exposure, how to set safe rules, and how to check the damage. It stays on Base44 entities. For the general theory of row-level rules, read the authorization patterns guide.
What does “Base44 app data exposed” mean?
It means that a person can read, change, or delete records that the person should not reach. The person can be an anonymous visitor or a signed-in user of another account.
A Base44 app stores its data in entities. An entity is similar to a table. A record is similar to a row. When you define an entity, Base44 gives your app create, read, update, and delete operations for it through the SDK. The Base44 entity overview describes this.
The app code runs in the browser. For this reason, the screen is not the security boundary. A user can call the same operations without your screens. The rules on the entity are the only control that decides what the call returns.
Base44 has two kinds of rules. These are from the Base44 entity security documentation:
| Rule type | What it controls |
|---|---|
| Row-level security (RLS) | Which records a user can create, read, update, or delete |
| Field-level security (FLS) | Which fields of a record a user can read or write |
A permission value can be true, false, or a condition. The value true means everyone. The value false means no one. A condition matches users or records by a test.
The risk is in the default. The Base44 entities-create reference says: “If no RLS is defined, all records are accessible to all users.” An AI builder creates an entity to make a feature work. If the builder adds no rule, the entity is open. The app looks correct, because you see your own data.
How do I check if my Base44 data is exposed?
Test the live app as a different person from the owner. The owner account often has extra access, so owner tests prove nothing.
Test 1: Use two ordinary accounts
- Create account A and account B in your published app. Use two different email addresses.
- Sign in as account A. Create a record in each feature, such as a note, an order, or a profile.
- Sign out. Sign in as account B.
- Open each list screen and each detail screen.
- Look for records that account A created.
If account B can see a record of account A, the read rule of that entity is too open. Repeat the test for every entity that holds user data.
Test 2: Use a signed-out browser
- Open a private browser window. Do not sign in.
- Open the app and each page that you can reach.
- Open DevTools and select the Network tab.
- Look for requests that return lists of records.
- Check if the response holds records without a sign-in.
Some entities must be public, such as a list of blog posts. Other entities must never be public, such as orders or messages. Write down which is which before you test.
Test 3: Replay a request without the token
- Sign in as account B and open a page that loads a list.
- In the Network tab, find the request that returns the list.
- Select Copy as cURL.
- Remove the
Authorizationheader from the copied command. - Run the command in a terminal.
If the server returns the records, the entity is readable without a sign-in. Do not paste real tokens into a chat or a ticket.
Test 4: Read the entity files
If you use the Base44 CLI or a synced project, open the entity schema files. Look for an rls key on each entity. An entity with no rls key is open to all users, according to the docs. An entity with "read": true is open to read for everyone.
Warning: Do not test with real customer data in a way that changes or deletes records. Use test records that you created.
What do I do first? Fix it now, in order of urgency
Do the steps in this order. The first steps stop new reads. The other steps make the fix permanent.
Caution: Stricter rules can break screens that relied on open access. This is the correct result when the data is private. Test each screen after you push the rules.
- List your entities. Write the name of each entity and mark the ones that hold personal, financial, or private data.
- Make the private entities safe first. Start with users, orders, messages, payments, and uploads. Set the read rule to the owner or to admins.
- Take down secrets stored in records. If a record holds an API key, a token, or a password, delete the value and rotate it at the provider. Anyone who could read the record could copy the value.
- Add rules for all four operations. Set
create,read,update, anddelete. A rule forreadalone does not stop a user from changing or deleting records. - Push the rules. The docs say to deploy security rules with
entities push. They also deploy with the full projectdeploycommand. - Test again with two accounts. Repeat Test 1 and Test 3 from the previous section.
- Add field-level rules for sensitive fields. Use them for values that only admins should see.
- Check the damage. See the section about damage later in this guide.
If you cannot push rules at once, consider whether you can take the app offline until you can. An app that is offline cannot leak more data. Check the current Base44 help pages for the exact steps.
How do I write safe RLS rules for a Base44 entity?
The rules live in the rls key of the entity schema. The Base44 security documentation gives this form:
"rls": {
"create": true,
"read": {"created_by": "{{user.email}}"},
"update": {"created_by": "{{user.email}}"},
"delete": {"user_condition": {"role": "admin"}}
}
Read the rule line by line. Anyone can create a record. A user reads and updates only records where created_by equals the email of the current user. Only a user with the role admin can delete.
The field created_by holds the email of the user who created the record. The template {{user.email}} is the email of the user who makes the request. A rule that compares the two makes each record private to its creator.
Pattern 1: Private to the owner
Use this pattern for notes, orders, settings, and anything that belongs to one user. The Base44 RLS examples show a private notes entity:
"rls": {
"create": true,
"read": {"created_by": "{{user.email}}"},
"update": {"created_by": "{{user.email}}"},
"delete": {"created_by": "{{user.email}}"}
}
Pattern 2: Admin only
Use this pattern for contact form submissions and internal data. Anyone can submit. Only admins can read:
"rls": {
"create": true,
"read": {"user_condition": {"role": "admin"}},
"update": {"user_condition": {"role": "admin"}},
"delete": {"user_condition": {"role": "admin"}}
}
Pattern 3: Public read, owner write
Use this pattern for blog posts and public listings. Everyone reads. Only the creator edits:
"rls": {
"create": true,
"read": true,
"update": {"created_by": "{{user.email}}"},
"delete": {"created_by": "{{user.email}}"}
}
Caution: The pattern with "create": true lets any visitor create records. Decide if you want this. If only signed-in users must create records, ask the Base44 docs or the app editor how to require a signed-in user for that operation. Test the rule with a signed-out request after you set it.
Rule limits that cause mistakes
The RLS examples page lists limits that you must know:
user_conditionsupports only equality checks. It does not support operators.- The operators
$in,$nin,$ne, and$allwork only ondata.*fields. - The operators
$gt,$lt,$gte,$lte, and$regexare not supported. - Cross-entity relationships are not supported. The docs say to use backend functions for these cases.
The docs also say that a service-role call does not bypass RLS. Your rules must include admin access for service-role operations to succeed. If a backend function fails after you add rules, check this first.
Field-level rules
Add an rls key to a field when only some users may read it. The docs show this form:
"salary": {
"type": "number",
"rls": {
"read": {"user_condition": {"role": "admin"}},
"write": {"user_condition": {"role": "admin"}}
}
}
A field with no rule follows the rules of its entity. Use field rules for personal data that must stay hidden even from users who can read the record.
How do I check the damage after Base44 data was exposed?
Check what a reader could reach, and check if anyone changed records. The steps depend on which operations were open.
- List the open entities and operations. An entity with no rules was open for read, update, and delete. Write down each one.
- Look for unknown users. Check the user list for accounts that you do not know. Look at sign-up dates that match the time of the exposure.
- Check records by
created_by. Look for records that were created by unknown emails. Look for records with odd content. - Check updated and deleted records. Compare the current data with a backup or an export. Look for missing records and for changed values, such as a role or a price.
- Check stored secrets. Look in your records for tokens, keys, and passwords. Rotate each one.
- Read any logs that your plan provides. If the Base44 dashboard shows request logs, look for bulk reads of one entity. If no logs exist, you cannot prove what was read.
- Check backend function logs. Look for calls that you did not expect.
If you cannot prove that nobody read the data, assume that someone did. Treat the personal data in the open entities as exposed. This can start legal duties, depending on your users and your country. See the last section.
Read more about this type of bug in broken object-level authorization in AI-generated apps. Users who can write records can also try to set fields that they must not control. See mass assignment.
How do I stop the next exposure?
Use these controls for each new feature.
- Write the rule when you create the entity. Do not publish an entity with no
rlskey. Add a rule forcreate,read,update, anddeleteat the start. - Start closed and open later. Set
falseor an owner rule first. Open an operation only when you have a reason. - Test with two accounts before each release. Repeat Test 1 for each new entity.
- Test with a signed-out browser. Repeat Test 2 for each new entity.
- Do not store secrets in entities. Keep API keys in backend function secrets, not in records. A user who can read the record can read the key.
- Use backend functions for logic that rules cannot express. Put the permission check on the server side of the function.
- Review prompts that change data access. When you ask the AI builder to add a feature, read the entity changes in the result. Look for
"read": true. - Scan the published app after each release. A scan tests the real app, not the intention in your prompt.
Read the Base44 platform guide for more checks. Read the Base44 security scanner overview to see what an automated test looks at.
When should I get help?
Get help in these cases:
- The exposed entities held personal, health, or payment data. Ask a security engineer or a lawyer about your duty to report.
- You cannot tell which records were read. Treat all of the records in the open entities as exposed.
- Your rules need cross-entity checks that Base44 rules do not support. A backend function can do the check. Ask an engineer to review it.
- Your app lets users share records with teams or groups. Group rules are easy to get wrong, so test them with several accounts.
- Base44 support must change something that you cannot change. Contact them with the app ID, the entity names, and the dates.
After you finish, run the two-account test one more time on the published app. Confirm that account B sees none of the records of account A, and that a signed-out browser sees none of the private entities.
Common questions
Is Base44 data private by default?
What is RLS in Base44?
Does a hidden page in the app protect the data?
Do I need field-level security too?
Can I fix this without changing the app screens?
Test your base44 data
A VibeEval scan opens your published Base44 app, tests which entity records a visitor or a second account can read, and reports the entities that return other users' data.
14-day free trial · No credit card · Cancel anytime