r := mux.NewRouter()
usersR := r.PathPrefix("/users").Subrouter()
usersR.Path("").Methods(http.MethodGet).HandlerFunc(getAllUsers)
usersR.Path("").Methods(http.MethodPost).HandlerFunc(createUser)
usersR.Path("/{id}").Methods(http.MethodGet).HandlerFunc(getUserByID)
usersR.Path("/{id}").Methods(http.MethodPut).HandlerFunc(updateUser)
usersR.Path("/{id}").Methods(http.MethodDelete).HandlerFunc(deleteUser)

fmt.Println("Start listening")
fmt.Println(http.ListenAndServe(":8080", r))
}

因此,我們可以將注意力集中在 golang 破壞的對象級授權上,我們將在示例代碼中采取一些捷徑。 

我們不會檢查代碼來創建、驗證和管理安全的用戶會話,而是使用單個模擬來確認當前會話是否有效。 

此外,我們不會查看 SQL 或 NoSQL 代碼,而是使用模擬來獲取和設置用戶信息。 

查看用戶

因此,這是getUserById處理程序。 

func getUserByID(w http.ResponseWriter, r *http.Request) {

valid, err := checkSession(r)
if valid == false {
fmt.Println(err)
http.Error(w, "Invalid user session!", http.StatusMethodNotAllowed)
return
}

id := mux.Vars(r)["id"]
u, err := getUserFromStore(id)
if err != nil {
http.Error(w, "Error retrieving user", http.StatusInternalServerError)
return
}

w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(&u); err != nil {
fmt.Println(err)
http.Error(w, "Error encoding response object", http.StatusInternalServerError)
}
}

因此我們可以使用 cURL 查看 BOLA 漏洞,我們將使用單個標頭來模擬有效會話。

egoebelbecker@genosha-2 ~ % curl -H "Session_ID: rekcebleboeg" localhost:8080/users/1
{"id":"1","lastname":"Doe","firstname":"John","dept":"","email":"jdoe@demo.com"}

由于此 GET 處理程序僅驗證請求用戶是否具有有效會話,因此任何能夠創建會話的人都可以通過 Id 請求用戶。 

由于我們已經兩次描述了這個錯誤,并且我們正在查看調用名為 checkSession()的方法的代碼,因此這個錯誤似乎很明顯。但情況并非總是如此,這是一個常見的設計錯誤。許多 API 被設計為在 Web GUI 和移動應用程序之間共享。當應用程序缺少允許您查看其他人信息的 GUI 元素時,它們會隱式執行對象級授權。有時它們甚至被構造為只有一個應用程序可以執行某些功能。 

但攻擊者會查看 Web 源代碼并逆向工程 API,可能只需幾分鐘。此 API 不會檢查訪問權限是否正確,并且具有連續的用戶 ID。情況很糟糕。

查看所有用戶

假設管理用戶擁有不同的 Web 界面。他們可以查看所有用戶。

func getAllUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")

valid, err := checkSession(r)
if valid == false {
fmt.Println(err)
http.Error(w, "Invalid user session!", http.StatusMethodNotAllowed)
return
}

var users []User
for key := range store.Keys(nil) {
u, err := getUserFromStore(key)
if err != nil {
http.Error(w, "Error retrieving user", http.StatusInternalServerError)
return
}
users = append(users, u)
}

if err := json.NewEncoder(w).Encode(users); err != nil {
fmt.Println(err)
http.Error(w, "Error encoding response object", http.StatusInternalServerError)
}
}

因此,攻擊者不需要使用連續的 ID 來獲取整個用戶數據庫。 

這是那個請求。(添加了一些格式。)

egoebelbecker@genosha-2 ~ % curl -H "Session_ID: rekcebleboeg" localhost:8080/users
[{"id":"1","lastname":"Doe","firstname":"John","dept":"HR","email":"jdoe@demo.com"},
{"id":"2","lastname":"Smith","firstname":"Jane","dept":"Dev","email":"jsmith@demo.com"},
{"id":"3","lastname":"Neuman","firstname":"Alfred E.","dept":"CEO","email":"whatme@worry.com"},
{"id":"4","lastname":"Goebelbecker","firstname":"Eric","dept":"Janitorial","email":"noreply@demo.com"}]

再次,應用程序設計人員依賴客戶端應用程序中的限制來阻止用戶看到他們不應該看到的信息。

Golang 損壞的對象級授權指南圖像

修復 Golang BOLA

那么我們該如何解決這些問題呢?有幾種不同的方法。讓我們簡要地看一下每種方法。 

錯誤的答案

一個答案是從 API URL 中刪除用戶 ID。

因此這五個端點可能看起來像這樣: 

這只會改變兩個端點,而所有漏洞都保留了下來。這可能會讓經驗豐富的黑客更難利用該 API。在對 cURL 進行幾分鐘的試驗后,它可能不會讓熟練的攻擊者感到困擾。 

還有更好的解決方案。 

添加對象級別授權

最好且唯一有效的解決方案是添加代碼來驗證用戶是否有權訪問他們想要創建、讀取、更新或刪除的對象。 

示例應用程序已有一個方法來驗證會話是否有效。為了遵守單一職責原則,讓我們添加一個新方法來檢查用戶是否可以執行他們要求的操作。?

有很多方法可以做到這一點。有些方法比其他方法更好,有些方法適合在關于 BOLA 的簡短博客文章中介紹,并且不是生產代碼。 

首先,讓我們定義用戶想要做什么以及他們是否可以做。枚舉非常適合此目的。 

定義操作和授權

type Action int64
const (
Read Action = 0
Write = 1
Update = 2
Delete = 3
Create = 4
List = 5
)

type Authorization int64
const (
Denied Authorization = 0
Allowed = 1
)

現在,我們可以編寫一種方法來詢問用戶是否可以執行他們要求的操作。

func checkAuthorization(requester string, action Action, target string) (auth Authorization) {

switch action {
case Read:
if requester == target || requester == admin {
return Allowed
}
break;
case Create:
case Update:
case Delete:
case Write:
case List:
if requester == admin {
return Allowed
}
}
return Denied
}

這是對象級授權的基本示例。該方法允許用戶讀取其信息,并且僅允許管理員用戶執行其他操作。但是,由于我們對請求的操作使用了枚舉,因此很容易為不同的操作添加不同級別的權限。因此,部門主管可以修改其團隊成員的記錄,或者人力資源團隊可以添加地址信息等。 

將其添加到 API

我們來看看新的getUserbyID: 

func getUserByID(w http.ResponseWriter, r *http.Request) {

valid, user, err := checkSession(r)
if valid == false {
fmt.Println(err)
http.Error(w, "Invalid user session!", http.StatusMethodNotAllowed)
return
}
id := mux.Vars(r)["id"]
var auth = checkAuthorization(user, Read, id)

if auth == Allowed {

u, err := getUserFromStore(id)
if err != nil {
http.Error(w, "Error retrieving user", http.StatusInternalServerError)
return
}

w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(&u); err != nil {
fmt.Println(err)
http.Error(w, "Error encoding response object", http.StatusInternalServerError)
}

} else {
http.Error(w, "Not permitted", http.StatusMethodNotAllowed)
return
}
}

我們必須修改checkSession以返回用戶 ID,這樣我們才能使用它來檢查權限。如果通過,該方法將像以前一樣繼續。如果沒有通過,它會向請求應用程序返回錯誤。 

現在,getAllusers看起來像這樣:

func getAllUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")

valid, user, err := checkSession(r)
if valid == false {
fmt.Println(err)
http.Error(w, "Invalid user session!", http.StatusMethodNotAllowed)
return
}

var auth = checkAuthorization(user, List, "")

if auth == Allowed {

var users []User
for key := range store.Keys(nil) {
u, err := getUserFromStore(key)
if err != nil {
http.Error(w, "Error retrieving user", http.StatusInternalServerError)
return
}
users = append(users, u)
}

if err := json.NewEncoder(w).Encode(users); err != nil {
fmt.Println(err)
http.Error(w, "Error encoding response object", http.StatusInternalServerError)
}
} else {
http.Error(w, "Not permitted", http.StatusMethodNotAllowed)
return
}
}

 由于我們嘗試使用 List 操作,因此 我們不需要將目標用戶傳遞給checkAuthorization 。

我們將checkAuthorization添加到此應用程序中的其他三個方法中以完成這項工作,并且我們擁有基本的對象級授權。隨著時間的推移,它可以發展成為一個更復雜的示例,具有多層授權實例,是一個粗略的基于角色的系統。 

附加

雖然我們已經修補了這個簡單 API 中的大漏洞,但我們可以采取更多措施使其更加強大:將用戶 ID 從數字更改為更難猜測的非連續格式,例如 UUID。雖然這不會解決代碼中的任何安全問題,但它確實使 API 成為不那么有吸引力的目標。

文章來源:Golang Broken Object Level Authorization Guide: Examples and Prevention

上一篇:

動態應用程序安全 測試(DAST)工具 概述和指南

下一篇:

.NET 損壞認證指南:示例和預防
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費