類程式設計的WAF(上)

天存資訊發表於2021-06-17

一、複雜的需求

WAF (WEB 應用防火牆) 用來保護 WEB 應用免受來自應用層的攻擊。作為防護物件的 WEB 應用,其功能和執行環境往往是複雜且千差萬別的,這導致即便防禦某個特定的攻擊方式時,使用者需求也可能是細緻而多樣的。

以最基本的 SQL 注入 (以下簡稱注入) 為例。注入攻擊當然是要防範的,但使用者可能還有以下需求:

  • 某個域名或某些特定的 URL 不需要注入檢查
  • 對來自外網的注入訪問進行攔截,來自內網的注入訪問只記錄不攔截
  • 對特定的請求引數名或特定特徵的請求引數不進行注入檢查
  • 非工作時段不僅攔截還阻止該使用者一段時間訪問
  • 對 admin 等管理賬號登入後的訪問不進行注入檢查
  • 對於只記錄不攔截的請求,附加一個特別的請求頭髮往應用
  • 對某些 URL 的注入訪問記錄下 HTTP 請求的全部報文
  • 將 HTTP 響應碼為 500 的注入的日誌緊急度設為 alert
  • ...

以上需求,使用者可能只提出一項,也可能提出多項,還可能是不同的邏輯組合或更多的條件和動作。這還僅僅是防注入這項基本功能,如果有更多的應用防護需求,比如:

一個已登入的非內網使用者在 10 秒鐘連續訪問 POST 方法的頁面 (非 AJAX 資料) 達到 5 次,則在 10 秒內延遲這個使用者的響應時間 0.5 秒;如果在未來 10 秒內繼續訪問 POST 方法頁面 3 次,則強制使用者登出並阻止登入 30 秒;如果一個使用者 1 天內這種情形發生 5 次,則產生一條嚴重級別的告警。

我們該如何描述滿足這些需求的功能呢?

WAF是否能夠設計得足夠靈活,使得實施人員通過現場配置就能實現這個需求?

二、規則的侷限性

大部分應用防火牆的配置以規則為核心。

傳統意義上的規則,其實質形式是獨立的一行行文字,每行文字有固定的結構/欄位,可以獨立地描述出一個功能。對使用者而言,書寫規則就是設定其中的引數和選項。這種規則的好處是簡單明瞭,使用者甚至可以在圖形化介面中完成規則的配置,但其弱點是不足以描述複雜的情形。

圖1

以防注入功能為例,如果它只有一個開啟或關閉的開關選項,或只能簡單地以區分站點來使用不同策略,顯然不能滿足前述的複雜需求。而企圖打造一個預先設定又包羅永珍的規則,則完全超出了“規則”的範疇,是不可能完成的任務。

追溯一下,用規則來描述防護功能始自於網路防火牆。網路防火牆的檢查物件是 TCP / IP 協議諸元,三/四層網路協議相對來說是簡單清晰的,用五元組就可以概括它們,以五元組為物件寫一些規則就能夠很好地實現防護。

圖2

但是,WEB 應用是怎麼寫成的?WEB 應用是用 Java / PHP / Python 等程式語言寫成的。就像不可能用“規則"來書寫 WEB 應用一樣,試圖良好地對應用進行防護,也不可能通過傳統的“規則"來實現——無論寫多少條規則。

三、大家一起來程式設計?

既然應用是程式設計的,那麼應用防火牆的配置可否也用程式設計的方式來實現?

以下是一個通用語言實現的例子,它的主功能是對請求引數進行注入檢查,檢查時會排除指定名字的引數,而且對不同來源訪問者 (外網或內網) 產生不同的日誌和動作:

for arg in ARGS:
    if arg['name'] in ['__utm', '__token']:
        return PASS

    if detect_sqli(remove_comment(url_decode(arg['value']))):
        if is_private_address(REMOTE_ADDRESS):
            log('SQLi and PASS')
            return PASS
        else:
            log('SQLi and BLOCK')
            return BLOCK
return PASS

功能是實現了,但看上去有點複雜。讓非程式設計師去寫這樣一段程式碼難免強人所難 (比如對集合型別資料的遍歷獲取),而且完全不可能做到視覺化。更重要的是,這僅是程式碼片段 (其實就是函式),真的要整合起來使用,還面臨很多程式設計方面的問題,如:

  • 除錯和容錯:
    各種語法錯誤和連結錯誤,比如使用了不存在的變數或方法。
  • 批量控制:
    怎樣實現全域性和批量的改變,比如想讓 WAF 全域性進入只記錄模式。
  • 作用域:
    每個程式碼片段有自己的作用域,如果想影響其他程式碼片段應該怎麼做?如何定義變數作用域?
  • 與預置防護集的關係:
    WAF 必然自帶預置的防護集,使用者書寫的程式碼與預置防護集的關係。
  • 引數的設定:
    應用相關的可配置引數怎麼處理,是作為常量寫在程式裡 (難以維護) 還是另作一個配置檔案 (程式變得更復雜)?

以上問題,如果都通過臨時修改程式碼 (全域性替換或加註釋) 來實現,則程式碼將變得不可維護。事實上,由於程式碼的無限可能性,甲寫的程式碼乙很難理解。為解決上述問題,必須要有一套程式框架,而框架本身的編寫、配置和使用又成了問題。

有沒有一種方法,不需要使用程式語言,而又能靈活滿足複雜的需求呢?

四、類程式設計的WAF

天存資訊的類程式設計 WAF,用資料結構來表達程式思想,讓普通的技術支援人員也能夠寫出足夠複雜和靈活的安全策略。

{
	"if": {
		"variables": "ARGS",
		"transform": "urlDecodeUni",
		"operator": "detectSQLi"
	},
	"then": {
		"verdict": {
			"action": "block",
			"log": true
		}
	}
} 

可見,這時的“規則”已經不是一行文字了,而是具有程式碼特徵的一個函式實現

類程式設計的 WAF 具有以下與程式語言相似的特性:

  • 無限巢狀的 if / the / else 條件判斷
  • 完整的 and / or / not 邏輯運算子
  • 對集合 / 陣列成員的遍歷運算
  • 變數包含多種資料型別
  • 支援變數的巨集擴充套件引用
  • 使用者自定義變數和表示式賦值
  • 預置及可設定不同生命期的全域性變數
  • 使用者書寫任意多樣的動作
  • 函式返回值靈活控制流程
  • 執行時改變其他函式行為

而這靈活內涵的表面,卻能夠用規範的模式 (schema) 來約束,使得寫出的類程式易讀且統一,甚至做到視覺化呈現。

相關文章