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 一起學 Python 共 101 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言