iT邦幫忙

2023 iThome 鐵人賽

0
Software Development

跟著 OXXO 一起學 Python系列 第 78

( Day 37.2 ) Python 設定 Firebase RealTime Database 安全規則

  • 分享至 

  • xImage
  •  

Firebase RealTime Database 的 Rules ( 安全規則 ) 可以讓資料庫具有多一層的安全保護,規則除了可以指定哪些資料可以讀取或寫入,也可以搭配 auth 根據使用者進行規範,這篇教學將會介紹如何設定 RealTime Database Rules 安全規則。

原文參考:設定 Firebase RealTime Database 安全規則

安全規則基本寫法

RealTime Database Rules 使用類似 JSON 格式的寫法,透過 .read.write .validate 定義是否可以存取和寫入。

類型 說明
.read 是否允許讀取資料,布林值判斷為 ture 可以讀取,false 不可讀取。
.write 是否允許寫入資料,布林值判斷為 ture 可以寫入,false 不可寫入。
.validate 判斷值、屬性、子屬性...等的正確格式。

如果是剛建立的專案,會使用「禁止讀取寫入」的設定,最外層是 rules 屬性,表示整份資料庫的規則,內容就會包含讀寫或資料夾相關的規則,因為 .read.write 都是 false,所以不論任何人都無法存取或寫入。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 禁止讀取寫入

如果將 .read.write 設為 true,表示任何人都可以寫入或讀取資料。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 可以寫入或讀取資料

此外,如果使用下面的寫法,會讓「有註冊」的用戶 ( 具備 uid ) 才能夠讀取、寫入或刪除資料。

{
  "rules": {
    ".read": "auth.uid != null",
    ".write": "auth.uid != null"
  }
}

定義規則順序

Firebase 的規則採用「淺層判斷至深層」的做法,只要是存取資料,規則的 true 與 false 一律由淺層 ( root 根節點 ) 開始判斷,舉例來說,使用規則模擬工具執行下列規則,a 節點的層級雖然寫了 false 阻擋寫入,但 root 節點卻允許寫入,就造成 a 節點仍然可以寫入資料的情形 ( 淺層的規則會覆蓋深層的規則 )。

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

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 淺層的規則會覆蓋深層的規則

如果不希望淺層的規則影響到淺層,可以「相關規則設定留空」( 不要指定 true 或 false ),如果不寫讀寫的規則,會變成預設值 false,false 不會影響深層,如此一來就會自動採用深層的設定,舉例來說,下方的規則指定了 a 節點不可讀取和寫入,b 節點則可以讀取和寫入,除了 b 節點之外,其他節點一率無法存取。

簡單來說,起始為 0,遇到 false 就加 0,遇到 true 就加 1,如果最後結果大於 0 就表示可以寫入或讀取

{
  "rules": {
    "a": {
      ".read": false,
      ".write": false
    },
    "b":{
      ".read": true,
      ".write": true
    }
  }
}

a 節點無法存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - a 節點無法存取

b 節點可以存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - b 節點可以存取

c 節點無法存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - c 節點無法存取

例如上面的例子,一開始如果是 true,不論哪層寫了 false,最後得到的值都一定大於 0,表示一定可以寫入或讀取,反之如果一開始是 0,遇到 true 加 1,在 a 節點就可以寫入或讀取,而其他節點因為沒有加 1 所以仍保持 0,就不能讀取或寫入了。

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

a 節點可以存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - a 節點可以存取

b 節點無法存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - b 節點無法存取

驗證 validate

「淺層判斷至深層」的原則只適用於單純撰寫 .read.write 規則,如果是規則中包含了 .validate,只會影響自己的層級,不受「淺層判斷至深層」的原則限制,例如下方的規則,在 root 節點會「驗證新資料一定要包含 a 節點」,所以如果儲存的資料包含 a 節點,就可以通過驗證,但在 a 的層級卻會被「驗證新資料一定要是數字格式」所阻擋,導致資料一定得是 a 節點下的數字資料才能儲存的狀況。

{
  "rules": {
    ".read":true,
    ".write":true,
    ".validate": "newData.hasChildren(['a'])",
    "a": {
      ".read": false,
      ".write": false,
      ".validate": "newData.isNumber()"
    }
  }
}

如果資料是數字可以存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 資料是數字可以存取

如果資料是文字無法存取。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 資料是文字無法存取

預設變數 predefined variables

在安全規則裡,除了單純的撰寫節點名稱,還有以下幾種預設的變數,由於每個變數都有其特定用法,在「節點」的命名上,必須要避開這些名稱。

變數 說明 .read .write .validate
now 建立節點的時間 - - O
root 讀取或寫入資料之前,存在資料庫的根節點 O O -
newData 寫入資料之後會存在的資料,包含新的資料和原本的資料 - O O
data 讀取或寫入資料之前,存在資料庫的資料 O O O
$變數 通用變數符號,表示在某個節點內的所有節點 O O O
auth 身份驗證使用的變數 O O -
  • now

    now 表示「資料庫寫入的時間」,單位採用毫秒計算 ( 也就是從 1970 年 1 月 1 日到現在的毫秒數 ),下列的規則指定使用者寫入的資料,必須包含 time 的節點,節點內容為放置時的毫秒數,數值一定要在 now 之後。

    {
      "rules": {
        ".read":false,
        ".write":true,
        ".validate":"newData.child('time').val()<now"
      }
    }
    

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - now

  • root

    root 表示「根節點」,通常會搭配類似 .child 的指令來輔助判斷,下列的規則如果資料庫內有 a 節點,且 a 節點內的 aa 節點的值為 ok,才能夠讀取資料,透過這個方法也可以達到保護資料庫的效果。

    {
      "rules": {
        ".read":"root.child('a').child('aa').val() == 'ok'",
        ".write":true
      }
    }
    

    如果節點 a/aa 的值是 ok,則可以存取。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - root 可以存取

    如果節點 a/aa 的值不是 ok,則不可以存取。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - root 不可以存取

  • newData

    newData 表示「新資料」,新資料的判斷只適用 .write.vaildate,下方的規則,會限制新的資料一定得包含 a 和 b 節點才能寫入。

    {
      "rules": {
        ".read":true,
        ".write":"newData.hasChildren(['a','b'])"
      }
    }
    

    如果資料中有 a 和 b 兩個節點,可以存取。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 有 a 和 b 兩個節點,可以存取

    如果資料中只有 a 但沒有 b 節點,則不可以存取。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 只有 a 但沒有 b 節點,則不可以存取

  • data

    data 表示「已經存在的資料」,可以用在 .read.write .vaildate,下方的規則,會判斷目前資料庫中 a 節點是否存在 lock 為 true 的資料,如果有,則無法寫入,如果沒有就可以寫入。

    {
      "rules": {
        ".read":true,
          "a":{
            ".write":"data.child('lock').val() != true"
          }
      }
    }
    

    如果 a 裡沒有 lock 為 true 的節點,可以存取 。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 如果 a 裡沒有 lock 為 true 的節點

    如果 a 裡有 lock 為 true 的節點,不可以存取 。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 如果 a 裡有 lock 為 true 的節點

  • $變數

    「$變數」的意思是「通用變數符號」,也就是不論該層節點如何命名,使用「$變數」就代表該層所有的節點名稱,舉例來說,下方的規則雖然是$a,但表示得並不是 a 節點,而是在 root 下一層的「所有節點」都能寫入或讀取資料。

    {
      "rules": {
        ".read": false,
        ".write": false,
          "$a": {
            ".read": true,
            ".write": true
          }
      }
    }
    

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 通用變數符號

  • auth

    auth 用於「搭配 firebase 專案的註冊帳號功能」,透過判斷註冊的帳號、uid...等相關資訊,決定是否可以讀取或寫入資料,下方的規則,會判斷帳號的 uid 是否等於節點的名稱或者 uid 是否存在,如果不等於則不會寫入,如果 uid 相等則會寫入,如果 uid 不存在則會新建一個名稱為 uid 的節點。

    因為第一層是 .write,若判斷為 true 則後方不論 .write 規則為何都可以寫入,所以第二層使用 .validate 就能避免「淺層判斷至深層」( 請參考本篇文章上面的說明 )。

    {
    "rules": {
      ".write":"auth.uid != null",
      "$user": {
        ".read": true,
        ".validate":"$user === auth.uid"
        }
      }
    }
    

    如果沒有 uid ,在第一層就會被排除。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 如果沒有 uid

    如果某個 uid 的節點名稱不等於使用者的 uid 時,這個節點不能寫入資料。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 如果某個 uid 的節點名稱不等於使用者的 uid 時

    如果資料庫中已經有 uid 的節點,當節點名稱等於使用者的 uid 時,這個節點就可以寫入資料。

    Python 教學 - 設定 Firebase RealTime Database 安全規則 - 如果資料庫中已經有 uid 的節點

保護已存在的資料

透過使用者 auth 的規則,可以定義較為妥善的資料保護方法,但針對「匿名」的使用者,大致上可以定義以下的保護條件:

  • 不能讀取整個資料庫,只能讀取某個節點 ( 表示知道名稱才能讀取 )。
  • 不能刪除資料,只能不斷添加資料。

根據這兩個條件,定義以下的規則,在 root 層禁止讀取和寫入,在之後的每個節點都可以讀取,不過如果該節點有資料,則無法寫入。

{
  "rules": {
    ".read": false,
    ".write": false,
    "$data": {
      ".read": true,
      ".write": "!data.exists()",
    }
  }
}

假設資料庫中已經有了下方的這些資料:

test: {
  a: 123
  b: 456
  c: 789
}

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 假設資料庫中已經有了下方的這些資料

透過模擬可以發現,若要讀取整份 data 會被拒絕。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 讀取整份 data 會被拒絕

單獨讀取 a 節點的資料是被允許的。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 單獨讀取 a 節點的資料是被允許的

若要寫入資料到 a 節點,因為 a 節點已經有資料,所以無法寫入。

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 單獨讀取 a 節點的資料是被允許的

不過如果是全新的節點,就可以寫入資料 ( 等同於使用 push 的功能 )

Python 教學 - 設定 Firebase RealTime Database 安全規則 - 全新的節點,就可以寫入資料

小結

Firebase RealTime Database 的規則其實相當簡單,只是要熟悉其邏輯和運作方式,透過規則就能保護資料庫,也就能做出更彈性的應用了。

更多參考

更多教學

大家好,我是 OXXO,是個即將邁入中年的斜槓青年,我有個超過一千篇教學的 STEAM 教育學習網,有興趣可以參考下方連結呦~ ^_^


上一篇
( Day 37.1 ) Python 建立 Firebase RealTime Database
下一篇
( Day 37.3 ) Python 串接 Firebase RealTime Database 存取資料
系列文
跟著 OXXO 一起學 Python101
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言