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 破壞的對象級授權(quán)上,我們將在示例代碼中采取一些捷徑。 

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

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

查看用戶

因此,這是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 處理程序僅驗證請求用戶是否具有有效會話,因此任何能夠創(chuàng)建會話的人都可以通過 Id 請求用戶。 

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

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

查看所有用戶

假設(shè)管理用戶擁有不同的 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)
}
}

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

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

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"}]

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

Golang 損壞的對象級授權(quán)指南圖像

修復 Golang BOLA

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

錯誤的答案

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

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

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

還有更好的解決方案。 

添加對象級別授權(quán)

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

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

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

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

定義操作和授權(quán)

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
)

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

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
}

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

將其添加到 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,這樣我們才能使用它來檢查權(quán)限。如果通過,該方法將像以前一樣繼續(xù)。如果沒有通過,它會向請求應(yīng)用程序返回錯誤。 

現(xiàn)在,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添加到此應(yīng)用程序中的其他三個方法中以完成這項工作,并且我們擁有基本的對象級授權(quán)。隨著時間的推移,它可以發(fā)展成為一個更復雜的示例,具有多層授權(quán)實例,是一個粗略的基于角色的系統(tǒng)。 

附加

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

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

上一篇:

動態(tài)應(yīng)用程序安全 測試(DAST)工具 概述和指南

下一篇:

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

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

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

#AI深度推理大模型API

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

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