如何為應用程式加入角色型存取控制(RBAC)
要加入角色型存取控制,請為每位使用者指派一個或多個角色(例如管理員、編輯、檢視者),定義每個角色可以執行哪些動作,並在伺服器上對每個請求檢查這些權限,而不只是隱藏按鈕。此外,還要檢查正在存取的紀錄是否屬於使用者的帳戶或團隊。一開始先在資料庫中建立少數固定的角色,然後直接嘗試存取其他使用者的資料來進行測試。
身分驗證與授權
兩個常被混淆的詞:
- 身分驗證是你是誰?,也就是登入。請參閱如何為應用程式加入登入功能。
- 授權是你被允許做什麼?,也就是已登入的使用者可以存取哪些頁面、紀錄和動作。
RBAC 是最常見的授權方式。這點很重要:截至 2026 年 9 月,Broken Access Control(存取控制失效)在 OWASP Top 10:2025 中排名第一,這是廣泛使用的網頁應用程式最嚴重安全風險清單。常見的失誤包括:使用者透過更改網址中的 ID 讀取其他使用者的紀錄,或一般使用者呼叫僅限管理員使用的 API 路由。
選擇模型
| 模型 | 運作方式 | 適合 | 取捨 |
|---|---|---|---|
| 單一角色欄位 | users.role = admin / member | 只有兩到三種角色的小型應用程式 | 當人們需要在不同地方擁有不同角色時,會變得混亂 |
| 角色加權限 | 角色對應到具名權限(invoices.approve);程式碼檢查權限 | 規則會持續成長的應用程式 | 需要更多資料表,也要維護權限清單 |
| 依團隊(多租戶)的角色 | 成員關係資料表:使用者 + 組織 + 角色 | 人們屬於團隊或工作區的 SaaS | 每個查詢都必須限定在該組織範圍內 |
| 以屬性或關係為基礎 | 依紀錄屬性的規則(「作者可以編輯自己的文章」)或關係的規則(已分享給誰) | 文件、分享、複雜的工作流程 | 較難理解;可考慮專用的函式庫或服務 |
| 身分提供者的角色 | 你的登入服務商儲存角色或組織成員資格 | 已使用代管身分驗證服務的應用程式 | 與該服務商綁定;仍須在你的伺服器上執行檢查 |
實務上,大多數應用程式會結合兩件事:角色決定某人可以採取哪種動作,擁有權決定他可以對哪些紀錄採取動作。「編輯者可以編輯文章」是角色規則;「……位於自己工作區中的文章」則是擁有權規則。兩者你都需要。
先從比你想像中更簡單的開始。管理員、成員和檢視者,就足以涵蓋相當多的應用程式。之後拆分角色,比解開十種沒有人理解的角色容易得多。
寫程式碼之前先設計角色
先用白話文寫一份權限表。它同時是交給 AI 的規格,也是你的測試計畫:
| 動作 | 擁有者 | 管理員 | 成員 | 檢視者 |
|---|---|---|---|---|
| 檢視團隊中的專案 | ✓ | ✓ | ✓ | ✓ |
| 建立與編輯專案 | ✓ | ✓ | ✓ | |
| 刪除專案 | ✓ | ✓ | ||
| 邀請與移除成員 | ✓ | ✓ | ||
| 變更角色 | ✓ | |||
| 帳單 | ✓ |
也要決定邊界情況:最後一位擁有者能否離開?管理員能否移除擁有者?被移除的使用者會看到什麼?
逐步執行
1. 先讓登入正常運作
RBAC 建立在身分驗證之上。伺服器必須能可靠地知道是誰發出每個請求,依據的是工作階段或已驗證的權杖,而絕不是瀏覽器在請求內文中傳來的使用者 ID。
2. 把角色儲存在資料庫中
如果是單一團隊的應用程式,在使用者資料表中放一個 role 欄位就足夠。若有團隊,請使用成員資料表:
create table memberships (
user_id uuid references users(id),
organization_id uuid references organizations(id),
role text not null check (role in ('owner','admin','member','viewer')),
primary key (user_id, organization_id)
);
絕不要讓使用者透過一般的個人資料更新來設定自己的角色。角色變更應透過專門的、經過權限檢查的端點進行。
3. 在伺服器上集中檢查權限
撰寫一個輔助函式,例如 can(user, 'projects.delete', project),並在每個讀取或修改資料的 API 路由、伺服器動作和背景工作中呼叫它。集中在一個函式中,比把檢查散落在程式各處更容易審查和測試。預設拒絕:如果沒有任何規則允許某個動作,就拒絕。
4. 限定每個查詢的範圍
每個查詢團隊資料時,都應包含團隊條件:where organization_id = :currentOrg,而不是只用 where id = :id。這可以防止有人把 /projects/123 改成 /projects/124 後,看到另一家公司的專案。對於其他團隊的紀錄,應回傳「找不到」而非「禁止存取」,以免洩露它們的存在。
5. 在關鍵處加入資料庫層級的保護
Postgres 的**列級安全性(RLS)**能讓資料庫強制執行每位使用者可以看到哪些資料列。如果瀏覽器會直接與資料庫溝通(例如某些代管後端),這點就至關重要;即使不是,它也是敏感資料表很有用的第二層防護。
6. 在介面中反映角色
現在可以隱藏或停用使用者無法執行的功能,讓應用程式不會令人困惑。這只是為了易用性;真正提供保護的是伺服器端的檢查。
7. 記錄敏感操作
記錄誰變更了角色、刪除了資料或匯出了紀錄,以及發生的時間。稽核紀錄有助於處理支援問題與安全事件。
8. 像攻擊者一樣測試
以每個角色登入,嘗試權限表中的每一個動作。然後再試那些本來應該失敗的情況:以檢視者身分呼叫管理員 API 路由、在網址和請求中更改 ID、移除自己作為最後一位擁有者。權限規則穩定後,為權限表撰寫自動化測試非常值得。
有效的提示詞
加入角色型存取控制。每個組織的角色:owner、admin、member、viewer,儲存在 memberships 資料表中。權限定義如下表:[貼上你的權限表]。建立一個伺服器端的 can(user, action, resource) 輔助函式,預設拒絕,並在每個 API 路由中使用。每個查詢都要以 organization_id 限定範圍。在介面中隱藏使用者無法執行的動作,但絕不要依賴這點來確保安全。
檢視這個應用程式中的每一個 API 路由和伺服器函式。對每一個,列出它讀取或修改了哪些資料、是否檢查使用者已登入、是否檢查其角色,以及是否把查詢限定在其組織範圍內。修正所有缺少檢查的部分,並把清單給我看。
加入僅限管理員使用的 /admin 頁面,讓擁有者可以變更成員的角色。禁止移除或降級最後一位擁有者,記錄每一次角色變更以及是誰在何時做的,並確保角色變更端點會拒絕任何不是擁有者的人。
常見錯誤
- **只在前端檢查角色。**API 仍然對任何呼叫它的人開放。
- 信任來自客戶端的 ID,例如在請求內文中放入
userId或role,而不是從工作階段取得。 - 未限定範圍的查詢,只用紀錄 ID 去抓取資料。
- 只在頁面上檢查角色,卻沒有在 API 路由、匯出、檔案下載或 Webhook 上檢查。
- 以硬編碼的電子郵件地址判斷管理員(
if email === 'me@...'),這很容易被遺忘,也很難修改。 - 太早建立太多角色,六個月後沒有人能說明清楚。
- 過期的工作階段:被降級的使用者在登出之前仍保有管理員權限。在敏感操作上,請從資料庫檢查角色,或重新整理角色資訊。
RBAC 檢查清單
- 已用白話文寫好權限表,並決定邊界情況
- 伺服器從工作階段得知使用者,而不是從請求資料得知
- 角色儲存在資料庫中;使用者無法自行變更
- 一個集中的權限檢查,預設拒絕,並用於每個路由
- 每個查詢都限定在使用者的組織或擁有權範圍內
- 瀏覽器能存取資料庫時,已啟用列級安全性
- 介面會隱藏使用者無法執行的動作(僅為易用性)
- 敏感操作已記錄
- 每個角色都已測試,包括直接呼叫 API 和更改 ID 的情況
存取控制只是更長清單中的一項;AI 建置應用程式的安全檢查清單涵蓋了其餘部分。如果你要為內部員工建置工具,如何建置內部工具說明了角色應該放在哪裡。
Mythex 上的 RBAC
Mythex 不為你應用程式的使用者提供內建的登入或角色系統。你可以選擇身分驗證函式庫或服務商,並描述所需的角色,代理人會把它們接入你的程式碼與專案的 Postgres 資料庫(為應用程式加入登入功能)。Mythex 自己的工作區角色(例如擁有者與成員)控制的是誰可以編輯你的 Mythex 專案,而不是誰可以使用你發布的應用程式。建置前請使用計畫模式先確認權限表,然後使用上面的檢視提示詞,並在發布前於預覽中測試每一個角色。文件中的安全性提示詞是很好的後續參考。
常見問題
什麼是角色型存取控制?
角色型存取控制(RBAC)會依據使用者所擁有的角色(例如管理員、編輯或檢視者)決定他們能做什麼。權限是附加在角色上,而不是個別的人身上,因此要改變某人的權限,只需要改變他的角色。
身分驗證和授權有什麼差別?
身分驗證確認某人是誰,通常透過登入完成。授權則決定已登入的人可以看見和做什麼。RBAC 是進行授權的一種方式。
只在介面上隱藏按鈕,足以限制存取嗎?
不夠。任何人都可以直接呼叫你的 API,因此隱藏按鈕只會改變人們看到的內容。每個讀取或修改資料的請求,都必須在伺服器上依據使用者的角色,以及對該筆紀錄的擁有權進行檢查。
我應該使用 Postgres 的列級安全性(row-level security)嗎?
列級安全性讓資料庫本身強制執行每位使用者可以讀取或寫入哪些資料列,是很強的安全網,特別是在前端直接與資料庫溝通時。但它會增加複雜度,因此許多應用程式會在伺服器程式碼中執行權限控制,並對敏感資料表另外使用列級安全性作為額外的一層防護。