SQL 注入之所以屢禁不止,核心在於「模板 + 拼接」的編程範式:開發者用字符串拼湊 SQL,攻擊者構造輸入打破語法邊界,改變語句語義。傳統防禦依賴「過濾、轉義、參數化」,但人為疏漏難以杜絕。本文探討一種根治思路:利用 SQL 語言的任意性,將模板轉換為攻擊者無法預測的等價語法體系,從語法層面徹底阻斷注入。
假設 SQL 模板:
select * from T where f1 = '{value1}' and f2 = {value2}
正常填充:value1=hello, value2=5 → select * from T where f1='hello' and f2=5
攻擊輸入:value1=anything' or 1=1 or f1='whatever
拼接後語義被篡改:
select * from T where f1='anything' or 1=1 or f1='whatever' and f2=5
SQL 採用中綴表示法,但存在多種等價的表示形式。將模板轉換為攻擊者不熟悉的語法,其注入載荷在新語法下將不合法。
-- 中綴:a OP1 b OP2 c ⇔ 前綴:(OP1 a (OP2 b c))
-- 模板轉換:
(select * T (and (= f1 '{value1}') (= f2 {value2})))
攻擊者注入後:
(select * T (and (= f1 'anything' or 1=1 or a='whatever') (= f2 5)))
語法錯誤!攻擊者需構造合法的前綴邏輯:(or (= f1 'anything') (or (= 1 1) (and (= a 'whatever') (= f2 5)))),但他不知道模板已轉換。
-- 中綴 ⇔ 歐拉:OP1(a, OP2(b, c))
select( *, T, and(=(f1,'{value1}'), =(f2,{value2})))
注入同樣導致語法錯誤,攻擊者無法構造正確的巢狀函數調用結構。
-- 前綴 ⇔ 物件:a.OP1(b).OP2(c)
T.where(f1.=('{value1}').and(f2.=({value2})).select(*)
注入語句:T.where(f1.=('anything' or 1=1 or a='whatever').and(f2.=5)).select(*) — 同樣語法失效。
SQL 關鍵字本質是任意 Token,可建立一組隨機映射表:
{
"select": "iph0ohKi", "*": "ieZoh4xa", "from": "aeZi5uja",
"where": "OoJ4aX4n", "=": "eeQu2Zad", "(": "eiD5aera",
")": "Soo2uach", "or": "Ocaig5Es"
}
操作數同理映射:T ⇔ @phai1Oa6@T@, hello ⇔ @phai1Oa6@hello@
原模板轉換後:
iph0ohKi ieZoh4xa aeZi5uja @phai1Oa6@T@ OoJ4aX4n @phai1Oa6@f1@ eeQu2Zad '{value1}' @phai1Oa6@and@ @phai1Oa6@f2@ eeQu2Zad {value2}
攻擊者注入的 or、= 等原生 Token 在此語法中均為非法,必須使用映射後的 Ocaig5Es、eeQu2Zad,但映射表僅後端持有,攻擊者無法構造合法語句。
模板 Token 數量固定(如 12 個)。填充後若 Token 總數變化(如變 20 個),即判定被注入。
-- 模板:[1]select [2]* [3]from [4]T [5]where [6]f1 [7]= [8]'{value1}' [9]and [10]f2 [11]= [12]{value2}
-- 注入後:變為 20 個 Token,違反不變量,直接攔截
此法可結合任意語法體系,雙重保險。
傳統方案依賴程序員「小心」,屬事後補救,終有疏漏。真正的解決之道在於:
這三層機制可自動化集成於中間件或 ORM 框架,實現「寫時無感、跑時免疫」,從根源上終結 SQL 注入攻防戰。