กฎความปลอดภัยพื้นฐาน

Firebase Security Rules ช่วยให้คุณควบคุมการเข้าถึงข้อมูลที่จัดเก็บไว้ได้ รูปแบบกฎที่ยืดหยุ่นช่วยให้คุณสร้างกฎที่ตรงกับทุกสิ่งได้ ตั้งแต่การเขียนทั้งหมดไปยังทั้งฐานข้อมูลไปจนถึงการดำเนินการในเอกสารที่เฉพาะเจาะจง

คู่มือนี้จะอธิบาย Use Case พื้นฐานบางอย่างที่คุณอาจต้องการนำไปใช้ขณะตั้งค่าแอปและปกป้องข้อมูล อย่างไรก็ตาม ก่อนเริ่มเขียนกฎ คุณอาจต้องดูข้อมูลเพิ่มเติมเกี่ยวกับภาษาที่ใช้เขียนกฎและลักษณะการทํางานของกฎ

หากต้องการเข้าถึงและอัปเดตกฎ ให้ทําตามขั้นตอนที่ระบุไว้ในจัดการและติดตั้งใช้งาน Firebase Security Rules

กฎเริ่มต้น: โหมดล็อก

เมื่อสร้างอินสแตนซ์ฐานข้อมูลหรือพื้นที่เก็บข้อมูลในคอนโซล Firebase คุณสามารถเลือกได้ว่าจะให้ Firebase Security Rules จำกัดการเข้าถึงข้อมูล (โหมดล็อก) หรืออนุญาตให้ทุกคนเข้าถึง (โหมดทดสอบ) ใน Cloud Firestore และ Realtime Database กฎเริ่มต้นสำหรับโหมดที่ล็อกจะปฏิเสธการเข้าถึงสำหรับผู้ใช้ทั้งหมด ใน Cloud Storage เฉพาะผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์เท่านั้นที่จะเข้าถึงที่เก็บข้อมูลได้

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}
{
  "rules": {
    ".read": false,
    ".write": false
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

กฎของสภาพแวดล้อมการพัฒนาซอฟต์แวร์

ขณะพัฒนาแอป คุณอาจต้องการการเข้าถึงข้อมูลแบบเปิดหรือแบบไม่จำกัด อย่าลืมอัปเดต Rules ก่อนนำไปใช้งานจริง นอกจากนี้ โปรดทราบว่าเมื่อคุณทำให้แอปใช้งานได้ ผู้ใช้จะเข้าถึงแอปนั้นได้แบบสาธารณะ แม้ว่าคุณจะยังไม่ได้เปิดตัวแอปก็ตาม

โปรดทราบว่า Firebase อนุญาตให้ไคลเอ็นต์เข้าถึงข้อมูลของคุณโดยตรง และ Firebase Security Rules เป็นมาตรการป้องกันเพียงอย่างเดียวที่บล็อกการเข้าถึงของผู้ใช้ที่เป็นอันตราย การกําหนดกฎแยกจากตรรกะผลิตภัณฑ์มีข้อดีหลายประการ ได้แก่ ลูกค้าไม่ต้องรับผิดชอบในการบังคับใช้การรักษาความปลอดภัย การติดตั้งใช้งานที่มีข้อบกพร่องจะไม่ทําให้ข้อมูลของคุณตกอยู่ในความเสี่ยง และที่สำคัญที่สุดคือคุณไม่ต้องอาศัยเซิร์ฟเวอร์สื่อกลางเพื่อปกป้องข้อมูลจากโลกภายนอก

ผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ทั้งหมด

แม้ว่าเราจะไม่แนะนำให้ผู้ใช้ที่ลงชื่อเข้าใช้สามารถเข้าถึงข้อมูลของคุณได้ แต่การกำหนดสิทธิ์เข้าถึงให้กับผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ขณะที่คุณพัฒนาแอปก็อาจมีประโยชน์

service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow read, write: if request.auth != null;
    }
  }
}
{
  "rules": {
    "some_path": {
      ".read": "auth.uid !== null",
      ".write": "auth.uid !== null"
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    match /some_folder/{fileName} {
      allow read, write: if request.auth != null;
    }
  }
}

กฎที่พร้อมใช้งานจริง

เมื่อเตรียมที่จะทําให้แอปใช้งานได้ ให้ตรวจสอบว่าข้อมูลได้รับการปกป้องและผู้ใช้ได้รับสิทธิ์เข้าถึงอย่างเหมาะสม ใช้ Authentication เพื่อตั้งค่าการเข้าถึงตามผู้ใช้และอ่านจากฐานข้อมูลโดยตรงเพื่อตั้งค่าการเข้าถึงตามข้อมูล

ลองเขียนกฎขณะจัดโครงสร้างข้อมูล เนื่องจากวิธีตั้งค่ากฎจะส่งผลต่อวิธีจํากัดการเข้าถึงข้อมูลในเส้นทางต่างๆ

สิทธิ์เข้าถึงสำหรับเจ้าของเนื้อหาเท่านั้น

กฎเหล่านี้จำกัดการเข้าถึงไว้สำหรับเจ้าของเนื้อหาที่ตรวจสอบสิทธิ์แล้วเท่านั้น ข้อมูลดังกล่าวจะมีเพียงผู้ใช้รายเดียวที่อ่านและเขียนได้ และเส้นทางข้อมูลจะมีรหัสของผู้ใช้

กรณีที่กฎนี้ใช้งานได้: กฎนี้ใช้งานได้ดีหากมีการแยกข้อมูลตามผู้ใช้ กล่าวคือ ผู้ใช้เพียงคนเดียวที่ต้องเข้าถึงข้อมูลได้คือผู้ใช้ที่สร้างข้อมูลนั้น

กรณีที่กฎนี้ใช้งานไม่ได้: ชุดกฎนี้จะไม่ทำงานเมื่อผู้ใช้หลายคนต้องเขียนหรืออ่านข้อมูลเดียวกัน ผู้ใช้จะเขียนทับข้อมูลหรือเข้าถึงข้อมูลที่ตนสร้างขึ้นไม่ได้

วิธีตั้งค่ากฎนี้คือ สร้างกฎที่ยืนยันว่าผู้ใช้ที่ขอสิทธิ์เข้าถึงเพื่ออ่านหรือเขียนข้อมูลคือผู้ใช้ที่เป็นเจ้าของข้อมูลนั้น

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{userId}/{document} {
      allow read, write: if request.auth != null && request.auth.uid == userId
    }
  }
}
{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth !== null && auth.uid === $uid",
        ".write": "auth !== null && auth.uid === $uid"
      }
    }
  }
}
// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

การเข้าถึงแบบผสมระหว่างสาธารณะและส่วนตัว

กฎนี้อนุญาตให้ทุกคนอ่านชุดข้อมูลได้ แต่จะจำกัดความสามารถในการสร้างหรือแก้ไขข้อมูลในเส้นทางที่ระบุไว้สำหรับเจ้าของเนื้อหาที่ตรวจสอบสิทธิ์เท่านั้น

กรณีที่กฎนี้ใช้งานได้: กฎนี้เหมาะกับแอปที่ต้องมีองค์ประกอบที่อ่านได้แบบสาธารณะ แต่ต้องจำกัดการเข้าถึงการแก้ไขไว้สำหรับเจ้าขององค์ประกอบเหล่านั้น เช่น แอปแชทหรือบล็อก

กรณีที่กฎนี้ใช้ไม่ได้: เช่นเดียวกับกฎสำหรับเจ้าของเนื้อหาเท่านั้น ชุดกฎนี้จะใช้ไม่ได้เมื่อผู้ใช้หลายคนต้องแก้ไขข้อมูลเดียวกัน ผู้ใช้จะเขียนทับข้อมูลของกันและกันในท้ายที่สุด

วิธีตั้งค่ากฎนี้: สร้างกฎที่เปิดใช้สิทธิ์การอ่านสําหรับผู้ใช้ทุกคน (หรือผู้ใช้ที่ตรวจสอบสิทธิ์แล้วทั้งหมด) และยืนยันว่าผู้ใช้ที่เขียนข้อมูลเป็นเจ้าของ

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      // Allow public reads
      allow read: if true
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;
      // Allow deletion if the current user owns the existing document
      allow delete: if request.auth.uid == resource.data.author_uid;
    }
  }
}
{
// Allow anyone to read data, but only authenticated content owners can
// make changes to their data

  "rules": {
    "some_path": {
      "$uid": {
        ".read": true,
        // or ".read": "auth.uid !== null" for only authenticated users
        ".write": "auth.uid === $uid"
      }
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}

สิทธิ์เข้าถึงตามแอตทริบิวต์และตามบทบาท

คุณต้องกําหนดและกำหนดแอตทริบิวต์ให้กับผู้ใช้ในข้อมูลเพื่อให้กฎเหล่านี้ทำงานได้ Firebase Security Rules ตรวจสอบคำขอเทียบกับข้อมูลจากข้อมูลเมตาของฐานข้อมูลหรือไฟล์เพื่อยืนยันหรือปฏิเสธการเข้าถึง

กรณีที่กฎนี้ทำงาน: หากคุณกําลังกําหนดบทบาทให้กับผู้ใช้ กฎนี้จะช่วยให้คุณจํากัดการเข้าถึงตามบทบาทหรือกลุ่มผู้ใช้ที่เฉพาะเจาะจงได้ เช่น หากจัดเก็บคะแนน คุณสามารถกำหนดระดับการเข้าถึงที่แตกต่างกันให้กับกลุ่ม "นักเรียน" (อ่านเนื้อหาเท่านั้น) กลุ่ม "ครู" (อ่านและเขียนในวิชา) และกลุ่ม "ครูใหญ่" (อ่านเนื้อหาทั้งหมด)

กรณีที่กฎนี้ใช้ไม่ได้: ใน Realtime Database และ Cloud Storage กฎของคุณจะใช้เมธอด get() ที่กฎ Cloud Firestore นำมาใช้ร่วมกันไม่ได้ คุณจึงต้องจัดโครงสร้างฐานข้อมูลหรือข้อมูลเมตาของไฟล์ให้สอดคล้องกับแอตทริบิวต์ที่คุณใช้ในกฎ

วิธีตั้งค่ากฎนี้: ใน Cloud Firestore ให้ใส่ช่องในเอกสารของผู้ใช้ที่คุณอ่านได้ จากนั้นจัดโครงสร้างกฎให้อ่านช่องนั้นและมอบสิทธิ์เข้าถึงแบบมีเงื่อนไข ใน Realtime Database ให้สร้างเส้นทางข้อมูลที่กําหนดผู้ใช้ของแอปและมอบหมายบทบาทให้ผู้ใช้เหล่านั้นในโหนดย่อย

นอกจากนี้ คุณยังตั้งค่าการอ้างสิทธิ์ที่กําหนดเองใน Authentication แล้วดึงข้อมูลดังกล่าวจากตัวแปร auth.token ใน Firebase Security Rules ใดก็ได้

แอตทริบิวต์และบทบาทที่กําหนดโดยข้อมูล

กฎเหล่านี้ใช้ได้ใน Cloud Firestore และ Realtime Database เท่านั้น

Cloud Firestore Realtime Database

โปรดทราบว่าทุกครั้งที่กฎมีการอ่าน เช่น กฎด้านล่าง ระบบจะเรียกเก็บเงินจากคุณสำหรับการดำเนินการอ่านใน Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, Check a boolean `admin` attribute
    allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    match /some_collection/{document} {
     allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Reader"
     allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Writer"
   }
  }
}
{
  "rules": {
    "some_path": {
      "${subpath}": {
        //
        ".write": "root.child('users').child(auth.uid).child('role').val() === 'admin'",
        ".read": true
      }
    }
  }
}

แอตทริบิวต์และบทบาทการอ้างสิทธิ์ที่กำหนดเอง

หากต้องการใช้กฎเหล่านี้ ให้ตั้งค่าการอ้างสิทธิ์ที่กําหนดเองใน Firebase Authentication แล้วใช้การอ้างสิทธิ์ในกฎ

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, check for an administrator claim
    allow write: if request.auth.token.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    match /some_collection/{document} {
     allow read: if request.auth.token.reader == "true";
     allow write: if request.auth.token.writer == "true";
   }
  }
}
{
  "rules": {
    "some_path": {
      "$uid": {
        // Create a custom claim for each role or group
        // you want to use
        ".write": "auth.uid !== null && auth.token.writer === true",
        ".read": "auth.uid !== null && auth.token.reader === true"
      }
    }
  }
}
service firebase.storage {
  // Allow reads if the group ID in your token matches the file metadata's `owner` property
  // Allow writes if the group ID is in the user's custom token
  match /files/{groupId}/{fileName} {
    allow read: if resource.metadata.owner == request.auth.token.groupId;
    allow write: if request.auth.token.groupId == groupId;
  }
}

แอตทริบิวต์ขององค์กร

หากต้องการใช้กฎเหล่านี้ ให้ตั้งค่าการรองรับผู้ใช้หลายรายใน Google Cloud Identity Platform (GCIP) แล้วใช้ข้อมูลเทนนต์ในกฎ ตัวอย่างต่อไปนี้อนุญาตให้เขียนจากผู้ใช้ในเทนนต์ที่เฉพาะเจาะจง เช่น tenant2-m6tyz

service cloud.firestore {
  match /databases/{database}/documents {
    // For tenant-based access control, check for a tenantID
    allow write: if request.auth.token.firebase.tenant == 'tenant2-m6tyz';
    allow read: true;
  }
}
{
  "rules": {
    "some_path": {
      "$uid": {
        // Only allow reads and writes if user belongs to a specific tenant
        ".write": "auth.uid !== null && auth.token.firebase.tenant === 'tenant2-m6tyz'",
        ".read": "auth.uid !== null
      }
    }
  }
}
service firebase.storage {
  // Only allow reads and writes if user belongs to a specific tenant
  match /files/{tenantId}/{fileName} {
    allow read: if request.auth != null;
    allow write: if request.auth.token.firebase.tenant == tenantId;
  }
}