0

I have a domain whitelist system to verify whether a site's domain is included in my whitelist. If a domain is on the list, it means the theme is verified and purchased. Otherwise, the code should not work.

My whitelist is stored in a Firebase Realtime Database JSON file at: https://sitelicenses-default-rtdb.firebaseio.com/domains.json

Problem: Currently, if someone accesses this URL directly https://sitelicenses-default-rtdb.firebaseio.com/domains.json, they can see the entire list of whitelisted domains. Instead, I want to restrict access so that:

If a request includes a specific domain as a parameter (e.g., https://sitelicenses-default-rtdb.firebaseio.com/domains.json/www.prothomalo-tenolent.blogspot.com), it should return only that domain if it's in the list. If accessed without a valid domain parameter (e.g., https://sitelicenses-default-rtdb.firebaseio.com/domains.json), nothing should be shown.

My JSON Data Structure in Firebase:

[
  "www.eatingfact-tenolent.blogspot.com",
  "www.prothomalo-tenolent.blogspot.com"
]

I am using Firebase Realtime Database, and my firebase rules is now:

{
  "rules": {
      ".read": true,
      ".write": true
    }
}

Is there a way to enforce this restriction using Firebase rules or any other method? Is it possible to achieve this behavior securely? Any guidance would be appreciated!

I tried to Restrict Firebase Realtime Database Access to Specific Query Parameters

1 Answer 1

1

Based on your question, your data is persisted in the RTDB as:

{
  "domains": {
    "0": "www.eatingfact-tenolent.blogspot.com",
    "1": "www.prothomalo-tenolent.blogspot.com",
    // ... more entries ...
  }
}

You could then apply the following rules to make it so this data cannot be queried and instead only accessed directly.

{
  "domains": {
    "$item": {
      // read only if accessed directly - range queries are disabled.
      ".read": "query.orderByKey && query.startAt === null && query.endAt === null",

      // write denied
      ".write": false
    }
  },
  // ... rules for other parts of your database ...
}

Note: See the Building Conditions section of the documentation for further details.

With this rule, you could make the following query:

$DATABASE_URL/domains/0.json
// returns `"www.eatingfact-tenolent.blogspot.com"`

Note: $DATABASE_URL is a placeholder representing your Firebase Config's databaseURL value.

But these queries would be blocked:

$DATABASE_URL/domains.json
// throws PERMISSION_DENIED

$DATABASE_URL/domains/.json
// throws PERMISSION_DENIED

Even though this form works, accessing 0.json, 1.json, and so on would be predictable. This can be solved by escaping the "." in each value and reversing the array (using values as keys with values set to true or 1).

{
  "domains": {
    "www%2eeatingfact-tenolent.blogspot%2ecom": true,
    "www%2eprothomalo-tenolent%2eblogspot%2ecom": true
  }
}

For an allowed domain, you would get back true when it is queried: ("%25" decodes to "%", so "%252e" decodes to "%2e")

$DATABASE_URL/domains/www%252eeatingfact-tenolent%252eblogspot%252ecom.json
// returns `true`

For an example, the above URL could be produced using the following JavaScript.

const domainToCheck = "www.eatingfact-tenolent.blogspot.com";
const encodedDomain = encodeURIComponent(domainToCheck.replaceAll(".", "%2e"))
const endpointUrl = "$DATABASE_URL/domains/" + encodedDomain + ".json"

For an unsupported domain, you would simply get back null.

$DATABASE_URL/domains/www%252efastingfact-tenolent%252eblogspot%252ecom.json
// returns `null`

If you instead wanted to simply block unsupported domains, you could instead make it so that the data must first exist in the rule.

{
  "domains": {
    "$item": {
      // read only if accessed directly and it exists in the db - range queries are disabled.
      ".read": "data.exists() && query.orderByKey && query.startAt === null && query.endAt === null",

      // write denied
      ".write": false
    }
  }
}
1
  • 1
    Great answer, Sam. 👍🔼 --- Did you test the rules in that last snippet though? Afaict those apply for a query on /domains/$item, while queries more likely run on /domains itself. --- Also: it might be worth adding an example of the security rules to the section where you converted the data structure to the map, so with ".read": false on domains and ".read": true on one level lower. Commented Mar 16 at 15:13

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.