APISIX 安全評估
背景
有大佬已經對apisix攻擊面[1]做過總結。
本文記錄一下自己之前的評估過程。
分析過程
評估哪些模塊?
首先我需要知道要評估啥,就像搞滲透時,我得先知道攻擊面在哪里。

根據文檔,可以知道apisix項目包括很多系統,包括:
- 網關[2]
- dashboard[3]
- ingress控制器[4]
- 各種sdk
sdk即使有漏洞,攻擊場景也感覺有限,所以沒有評估。
"ingress控制器"需要結合k8s中的網絡來做評估,因為時間有限,所以只是粗略看了一下。
我主要看了網關和dashboard兩個系統。

從文檔上很容易看出來,網關有三個重要的模塊:
- 插件
- admin api
- control api
對于api來說,首先要檢查的是"身份認證"和"鑒權"這兩個安全措施。
apisix歷史漏洞絕大部分都出現在插件中,所以插件屬于"漏洞重災區"。
評估api安全性:身份認證和鑒權
admin api實現如下:
- admin api 使用token做認證,token是硬編碼的。這個問題已經被提交過漏洞,官方應該不打算修復。
- admin api 鑒權上,設計了viewer和非viewer兩種角色。viewer角色只允許get方法。
靶場見 Apache APISIX 默認密鑰漏洞(CVE-2020-13945)
control api是沒有身份認證的,但是有兩個點限制了攻擊:
- 默認它只在本地監聽端口
- 插件無關的control api只有"讀信息"的功能,沒有發現啥風險點
插件創建的control api是一個潛在的攻擊面,不過我沒找到啥漏洞。
評估插件安全性
因為插件默認都是不開啟的,所以雖然它是重災區,但是我并沒有投入過多精力去審計。
不過在這里確實發現了一個安全問題,報告給官方后,分配了CVE-2022-25757。
下面來說一下這個安全問題。
CVE-2022-25757
這個安全問題是什么?
request-validation插件可以檢查HTTP請求頭和BODY內容,當不符合用戶配置的規則時,請求就不會轉發到上游。
比如用戶按照如下規則配置時,body_schema限制請求中必須要有string_payload參數,并且是字符串類型,長度在1到32字節之間。
curl http://127.0.0.1:9080/apisix/admin/routes/10 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri": "/10", "plugins": { "request-validation": { "body_schema": { "type": "object", "required": ["string_payload"], "properties": { "string_payload": { "type": "string", "minLength": 1, "maxLength": 32 } } } } }, "upstream": { "type": "roundrobin", "nodes": { "192.168.2.189:8888": 1 } } }'
但是惡意用戶發送如下請求時,有可能繞過限制
POST http://127.0.0.1:9080/10 ... {"string_payload":"","string_payload":"1111"}
為什么會繞過限制?
request-validation.lua中使用cjson.safe庫解析字符串為json對象,對于帶有"重復鍵值"的json,它會取最后面的值。比如{"string_payload":"","string_payload":"1111"},request-validation插件會認為string_payload="1111"。
local _M = { version = 0.1, decode = require("cjson.safe").decode,}
但是有很多流行的庫,對于帶有"重復鍵值"的json,它會取最前面的值,因此{"string_payload":"","string_payload":"1111"}會被認為string_payload=""。
因此request-validation插件和上游服務在解析json時可能存在差異性,所以會導致限制被繞過
哪些庫和request-validation插件在解析"重復鍵值json"時存在差異?
根據https://bishopfox.com/blog/json-interoperability-vulnerabilities 文章,可以知道最起碼以下庫和request-validation插件在解析"重復鍵值json"時存在差異。

選取其中的gojay庫做了驗證,程序打印gojay而不是gojay2
package mainimport "github.com/francoispqt/gojay"
type user struct { id int name string email string}// implement gojay.UnmarshalerJSONObjectfunc (u *user) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "id": return dec.Int(&u.id) case "name": return dec.String(&u.name) case "email": return dec.String(&u.email) } return nil}func (u *user) NKeys() int { return 3}
func main() { u := &user{} d := []byte(`{"id":1,"name":"gojay","email":"gojay@email.com"},"name":"gojay2"`) err := gojay.UnmarshalJSONObject(d, u) if err != nil { //log.Fatal(err) } println(u.name); // 取最前面的key的值,也就是gojay,而不是gojay2}
總結
評估思路比較簡單:
- 識別攻擊面
- api關注身份認證和鑒權
- 插件關注業務邏輯
openresty配置中的api也是攻擊面,下一篇再寫。
說一個題外話:apisix的插件機制提供了很好的擴展能力,再加上openresty的高性能,或許拿來做waf架構很合適。
參考鏈接
apisix攻擊面 :
https://ricterz.me/posts/2021-07-05-apache-apisix-attack-surface-research.txt
網關:
https://github.com/apache/apisix
dashboard:
https://github.com/apache/apisix-dashboard
ingress控制器:
https://github.com/apache/apisix-ingress-controller