SAST scanner · reads the rules files

Firebase Rules Analyzer

With Firebase the rules file is the whole server-side access control: the browser talks to the database directly, so a permissive rule is an open database. The analyzer reads firestore.rules, storage.rules and database.rules.json as text and reports rules that grant everything, rules with no auth test and broad wildcards.

SASTup to CriticalAuth Weakness3 file typesrepository only

A rules file, line by line

The scanner is the only actor and the target is a text file. Each step shows the pattern it runs, the line that answers it and the severity that comes back.

service cloud.firestore { match /databases/{database}/documents { match /users/{userId} { allow read, write: if true; } match /posts/{postId} { allow read: if true; allow write: if request.auth != null; allow delete: if isAdmin(); } match /{document=**} { allow read: if request.auth != null; which file, which service firestore.rules storage.rules database.rules.json content has "service cloud.firestore": Firestore "service firebase.storage": Storage; a .json file: Realtime Database these rules are the only access control: there is no backend to patch Firestore rules file, 12 lines read no finding yet: this step only classifies the file pattern tested allow\s+read\s*,\s*write\s*:\s*if\s+true matches line 4: read and write, with no condition at all reported once per file, with the first matching line number the user path does not matter: anyone on the internet qualifies CRITICAL · 0.95 Firebase Firestore rules allow unrestricted access pattern tested allow\s+read\s*:\s*if\s+true matches line 7: reads are open, writes are not a separate pattern does the same for write alone the comma form on line 4 cannot match this one, so there is no double count HIGH · 0.95 Firebase Firestore rules allow unrestricted access auth pattern request\.auth\s*!=\s*null |request\.auth\.uid looked at line 8 and the two lines after it found request.auth != null, so the line is accepted any signed-in user can write any post: that is not reported no finding silent by design: the check asks if an auth test exists, not what it tests pattern tested allow\s+(?:read|write|create|update|delete)\s*: line 9 and the next two lines hold no request.auth isAdmin() may check auth inside its definition: not followed the scanner reads text, so a helper hides the check from it HIGH · 0.85 Firebase Firestore rule missing authentication check pattern tested match\s+/\{[^}]+=\*\*\} line 11 is a catch-all: every path not matched deeper falls under it the allow inside accepts any signed-in user, so only the wildcard is reported reported once per wildcard match, with its line number MEDIUM · 0.75 Firebase Firestore rule uses broad wildcard

1The scanner picks a file whose path ends in firestore.rules, storage.rules or database.rules.json, then decides which service it is from the text. Rules are the only access control, because a Firebase client talks to the database directly.

2Pattern one is the literal allow read, write: if true. Critical, 0.95: anyone, signed in or not, can read and write whatever this match block covers.

3Pattern two is allow read: if true on its own; a third does the same for write. High, 0.95 for each, once per file. Open reads leak data but cannot be used to plant it.

4Every other allow line is checked for an auth test in a three-line window. request.auth != null satisfies it, so line 8 is accepted even though any signed-in user may write any post.

5allow delete: if isAdmin() has no request.auth within three lines. High, 0.85. The check is textual: it does not follow isAdmin() to its definition, so a helper that does verify auth is still reported.

6A match /{document=**} catch-all is reported at Medium, 0.75, because it may expose collections the author never meant to cover. The rule inside it is a plain auth check, so nothing else fires here.

Realtime Database

The older database keeps its rules in JSON. The analyzer treats the file as text and looks for exactly two shapes.

{ "rules": { ".read": true, ".write": "auth != null", "users": { "$uid": { ".read": "$uid === auth.uid" } } } } which file, which service database.rules.json a .json file is Realtime Database, whatever it contains the file is not parsed as JSON: the scanner runs two regular expressions on its text nested paths such as users/$uid are never walked Realtime Database rules, 9 lines read no finding yet: this step only classifies the file pattern tested "\.read"\s*:\s*true matches line 3: the whole database is readable by anyone the same file would also raise a second finding for ".write": true open read is Critical here, High in a Firestore file CRITICAL · 0.95 Firebase Realtime Database rules allow unrestricted access what stays silent ".write": "auth != null" ".read": "$uid === auth.uid" only the literal true is matched, a string expression is not evaluated so these conditions are never judged, good or bad one finding for the file, on line 3 1 finding an expression that is too broad, such as auth != null, passes unseen

1A path ending in .json is treated as Realtime Database rules. The text is searched, not parsed, so the nesting of the rule tree plays no part.

2".read": true grants the whole tree to the internet. Critical, 0.95. A literal ".write": true would be a second Critical finding.

3Rules written as strings, "auth != null" or "$uid === auth.uid", are never matched. Only the boolean literal true is flagged, so a too-broad expression is not reported here.

One rule, one request

A rule is a condition that Firebase evaluates against each request. Here is the same document read four times, with the verdict Firebase gives and the verdict isitsecure gives on the file. They differ in step 3, and that difference is what the check does not cover.

Request Anonymous visitorno tokenrequest.auth = null Bob, signed inuid = bobrequest.auth.uid = bob get /users/alice Rule on /users/{userId} allow read: if true;evaluates to true allow read: if request.auth != null;null != null is false allow read: if request.auth != null;bob != null is true allow read: if request.auth != null && request.auth.uid == userId;bob == alice is false Firestore decision ALLOWEDdocument returned to anyone DENIEDpermission-denied ALLOWEDBob reads Alice's document DENIEDonly the owner passes What isitsecure says about the rule It reads the rules file only. It never sends this request. HIGH: allow read: if true matches the open-read pattern.Anyone can read every document under /users. No finding, and that is right: the auth test is present and the visitor is refused.The scanner sees request.auth != null and moves on. No finding, but the rule lets every signed-in user read every user document.The scanner accepts any request.auth test. A missing ownership check is not reported. No finding, and that is right: owner-only access.This is the shape the markdown recommends for per-user data.

1An anonymous visitor asks for /users/alice. The rule is if true, so Firestore returns the document. isitsecure catches this one from the file alone: High.

2Same visitor, rule if request.auth != null. With no token request.auth is null, so the condition is false and Firestore refuses. The scanner stays quiet, correctly.

3Bob signs in and asks for Alice's document under the same rule. request.auth is not null, so Firestore allows it. The rule never compares the uid to the path. The scanner is silent here too, and this is the gap in the check.

4Add request.auth.uid == userId. Bob's uid is not Alice's, so the read is denied. This is the owner-only shape from the fix section, and the scanner accepts it.

Why it matters

Why it matters

Firebase security rules are the only access control for Firestore, Realtime Database and Storage. There is no backend API to add an auth check to. If the rules are permissive, anyone can read all data, anyone can write it, and nothing sits behind the database to stop them.

Real-world breach

Security researchers have published scans finding thousands of Firebase databases with open rules. In 2020 a study by Comparitech found 24,000 Android apps with misconfigured Firebase databases leaking user data, including emails, passwords and health data.

Vulnerable vs fixed rules

// BAD: wide open
match /users/{userId} {
  allow read, write: if true;
}

// BAD: no auth test anywhere near the allow
match /logs/{logId} {
  allow delete: if isAdmin();
}
Scope note

A missing auth test is reported, but request.auth != null with no ownership check is accepted. Missing rules for a collection and missing validation rules are not checked.

Limits and scope

BehaviourDetail
InputFiles whose path ends in firestore.rules, storage.rules, database.rules.json, or the same names under firebase/. The repository only; no live Firebase project is contacted.
Severity and confidenceOpen read and write together: Critical 0.95. Open read or write alone: High 0.95. Missing auth test: High 0.85. Wildcard: Medium 0.75. Realtime Database true rules: Critical 0.95 each.
Open rulesReported once per file and pattern, with the first matching line. Only the literal if true counts.
Missing auth testLooks at an allow read|write|create|update|delete: line and the two after it. Multi-operation lines such as allow read, write: if x do not match this pattern, so only the exact if true form of them is caught.
What counts as authrequest.auth != null or any mention of request.auth.uid. Ownership is not checked. Helper functions are not followed.
Realtime DatabaseTwo regular expressions on the text. String expressions are not evaluated.
Not implementedMissing rules for collections, data validation checks, ownership checks on auth != null.