firebasegoogle-cloud-firestorefirebase-security

Writing a firestore security rule for "Subscribers" collection


I have a simple website and I want to give users the option to subscribe to my mailing list. Nothing more, just entering their email, press "Subscribe" and their email gets stored into my "Subscribers" firestore collection.

This is my subscribe funstion:

export default async function subscribe(email) {
    if(!email || email.trim() ==""){
      return;
    }
    await setDoc(doc(db, "Subscribers", email), {
      date: Timestamp.fromDate(new Date()),
    });
}

Nothing fancy - just "Subscribers" collection, doc is unique email and I also save the date where users have subscribed. It works perfectly and I am happy with it.

The problem I have is with the security rules. Obviously I want to allow anyone to subscribe, and also if the user is already subscribed, I will just update the date in the "Subscribers" collection.

Those are my firestore rules:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /Subscribers/{email}{
        allow create: if request.resource.data.keys().hasOnly(['date'])
        allow update: if request.resource.data.keys().hasOnly(['date']);
        allow read: if false;
    }
  }
}

Those rules work fine - obviously it allows create and update and I am ok with that. However is my data safe? Is there anything else I can do to protect "Subscribers" collection from malicious attempts? Is there a way to verify that the request to to update doc with docId email has the same email as the docId?


Solution

  • As Doug already commented: "secure" is not an absolute state, and you always need to be explicit about what specific type of abuse/attack you want to secure/protect against.

    For example, based on the code/rules you shared you are using the user's email as the document ID, but nothing in your rules enforces that. So that means that a user can write under any email value they want, including a non-valid email or somebody else's email. Of that is something you want to prevent, you can do so with this additional clause in your create and update rules:

    email == request.auth.token.email
    

    This is just one specific scenario, and you'll need to identify each such scenario you want to secure/protect against.