背景
平常工做中會大量應用正則表達式,用正則表達式去定義規則,而後去匹配數據。這裏先看兩個安全場景下的正則應用需求正則表達式
場景1,FTP帳號被成功暴力破解後數據遭竊取緩存
• 數據源:FTP服務器日誌安全
• 關聯邏輯:針對特定帳號暴力破解,而後利用該特定帳號登陸成功,以後利用該特定帳號下載大量文件服務器
• 告警內容:FTP帳號${user_name}被成功暴力破解後竊取數據網絡
• 告警級別:高危框架
場景1中,正則表達式用於在日誌中匹配屢次帳戶登陸的行爲上。分佈式
場景2,Deep packet inspection (DPI) ,例如過濾網絡威脅和違反安全策略的流量等函數
• 數據源:網絡數據包工具
• 檢測規則條件:數據命中規則集性能
場景2中,正則表達式用於時間序列上的多個數據包之間的安全檢測。
其實,場景1中只列舉了FTP被攻擊的一種方式,FTP攻擊還有不少其餘手段,因此檢測FTP被攻擊的正則匹配場景的另外一個特徵就是整個規則集可能很大;場景2中,利用已知的入侵行爲構建模式集合,經過檢測網絡數據包,發現是否存在不符合安全策略的行爲或被攻擊的跡象,這須要對數據包的載荷部分進行檢測,要求匹配的速度很是快,不然將會影響用戶體驗。
另外一方面,這裏用到的正則與傳統用法又不太同樣,對正則的傳統用法是,給定一個文本,用一個或少數幾個正則規則,去匹配文本,找出文本中匹配的數據。而如今面對的問題,首先是規則的數量大,上千上萬或者超過十萬的規則集,若是仍然採用以前的作法,用|分割,或者外層用循環去匹配,那麼處理的時間將很長,對資源的消耗也很大,基本不可接受;其次在匹配的時候,待匹配的數據不是一個完整的總體,好比說網絡數據包,是一個一個接收的,這是一個流式的形式,傳統的正則處理引擎不能很好的處理流式數據,須要緩存一批數據去匹配,這樣匹配就不夠及時,並且目前正則處理有個很大的問題,若是正則表達式寫的很差,那麼匹配會很慢。因此,須要一個解決方案來應對如下這些挑戰:
• 規則數量多
• 匹配速度要快
• 支持流式數據
• 資源消耗不能太大
Hyperscan算子介紹
針對上述正則匹配中遇到的挑戰,通過調研和對比測試市面上的主流正則匹配引擎,咱們最終選擇了Hyperscan。
Hyperscan是Intel開源的高性能正則表達式匹配庫,提供了C語言API,目前已經在不少商業項目和開源項目中獲得應用。
Hyperscan具有這些特性:
• 支持大部分PCRE正則語法(若是使用Chimera庫,那將支持全部語法)
• 支持流式匹配
• 支持多模匹配
• 採用特定指令集加速匹配
• 易於擴展
• 內部多種引擎結合
Hyperscan在設計之初就是爲了更好的處理流式匹配和多模匹配,對流模式的支持極大的方便了正則用戶,再也不須要用戶去維護接收到的數據,無需緩存數據;多模匹配容許把多個正則表達式傳入並在同一時間進行匹配。
由於須要特定的指令集,因此Hyperscan對CPU有要求,以下圖:
CPU最低要支持SSSE3指令集,最下面一行的指令集能加速匹配
和大多數正則引擎相似,Hyperscan也包括編譯和匹配階段,編譯是把正則表達式解析而後構建成內部須要的database,後續能夠屢次使用這個database去匹配;若是是多模匹配,編譯時每一個正則表達式須要有一個惟一的標識id,id在匹配的時候會用到。編譯過程以下圖所示:
匹配的時候Hyperscan默認會返回全部命中的結果,這點不像有些正則引擎,指定貪婪的時候返回貪婪的匹配結果,指定懶惰的時候返回懶惰的結果。匹配時若是有命中,那麼會以回調函數的形式通知用戶在哪一個位置命中了哪一個正則表達式id。匹配過程以下圖所示:
Hyperscan的缺點是隻能是單機執行,沒有分佈式能力,其能夠解決延遲的問題,但沒法解決吞吐的問題,解決吞吐問題,能夠依靠主流實時計算框架Flink。Flink 是一個在無界和有界數據流上進行狀態計算的框架和分佈式處理引擎。無界就是有開始但沒有結束的數據,無界的數據流計算即流式計算,有界就是有開始有結束的數據,有界的數據流計算即批處理。
Flink能夠用於不少的計算場景中,這裏列舉了3個,Flink能夠處理事件驅動的程序,除了簡單事件,Flink還提供了CEP庫能夠處理復瑣事件;Flink還能夠做爲數據管道,作一些數據清洗篩選、轉換等操做,把數據從一個存儲系統轉移到另外一個系統;Flink能夠作流或批式數據的分析、指標計算,用於大屏展現等。Flink已經成爲業界公認的流式處理的第一選擇。
把正則匹配引擎整合到Flink中,藉助Flink強大的分佈式能力,強強聯合,那麼將會發揮更大威力。因此提供了這樣的解決方案,以下圖所示:
該解決方案實現了一個自定義的UDF算子,算子支持指定只匹配輸入數據中的某幾個字段,算子的輸出是待匹配的字段文本,匹配最終狀態,包括命中,不命中,錯誤,超時四種狀態,若是是命中的狀態,那麼還會返回匹配中的正則表達式的id,輸出還包括輸入原始數據,若是有後續處理,這樣不受影響;爲了進一步方便用戶使用,擴展了一個新的datastream,稱之爲Hyperscanstream,它把算子封裝進了進去,用戶在使用時只須要把datastream轉換爲Hyperscanstream,而後經過調用一個方法就可使用正則的算子了。整個解決方案以一個獨立的jar包提供給用戶,這樣能夠保持原來編寫Flink做業的習慣,而且與Flink的核心框架解耦。
數據流轉的過程是這樣,數據源讀取到一條記錄後交給下游的Hyperscan算子,Hyperscan算子把數據交給Hyperscan子進程,子進程匹配完成後把結果返回給Hyperscan算子,而後Hyperscan算子把原始記錄和匹配的結果傳遞給後續算子。
算子使用說明
私有化部署
針對私有化部署場景,用法以下,用戶首先須要去編輯正則表達式文件,而後用工具把正則表達式編譯爲database而且序列化爲本地文件,若是部署的環境中有HDFS,那麼能夠把序列化後的文件上傳至HDFS,若是沒有那就不用上傳,而後開發Flink做業,引用序列化的文件去匹配數據。
爲何要有工具編譯並序列化這一步呢,編輯完正則表達式,直接在Flink做業中使用不行嗎?前面說了,Hyperscan執行包括編譯和匹配階段,若是做業中只引用正則表達式,假設做業設置了並行度爲5,那麼每一個task都須要編譯一次,一共須要編譯5次,浪費資源;並且編譯在hyperscan中是個相對緩慢的動做,因此把編譯過程單獨出來也爲了加速flink做業在儘快執行。編譯提早進行也有利於提早知道正則表達式是否有語法錯誤,或者不支持的狀況,而不是做業啓動後才知道。
私有化部署時,hyperscan相關依賴程序會提供給用戶,依賴程序經過全靜態編譯而來因此無需再添加依賴,只需機器支持須要的指令集便可。
公司內部使用
使用示例
假設如今要匹配的是HTTP報文中的Host字段和Referer字段,以下圖所示:
代碼示例以下圖:
整個邏輯分爲四步,第一步要從數據源構建輸入流,第二步把輸入流轉換爲Hyperscanstream,第三步調用hyperscan方法進而使用Hyperscan算子,在第一個參數HyperscanFunction中指定要匹配的是Host和Referer字段,第四步使用匹配返回的結果,返回的結果是Tuple2對象,其中第一個字段Event是原始記錄,在本例中就是整個HTTP報文,第二個字段是HyperScanRecord組成的List,HyperScanRecord類中包括匹配的字段,例如本例中Host或Referer,匹配命中的正則表達式id(若是匹配命中的話)和匹配的最終狀態。
使用1萬條規則集以及不一樣大小的待匹配樣本通過測試,方案達到了指望的性能,測試結果以下圖,
使用Hyperscan算子的一些建議,以下圖:
前面提到,在不使用himera庫時,Hyperscan有部分PCRE語法不支持的狀況,在使用時要注意,下圖列舉了不支持的語法(使用Chimera庫將會影響匹配性能)
將來展望
一方面,目前Hyperscan算子在安全、威脅感知的場景中已經獲得運用,但但願能在更多場景中進行檢驗,理論上在全部正則匹配的場景中都能使用,比方說文本審覈、內容提取等。
另外一方面,也在提高Hyperscan算子易用性,比方說如今的規則發生變化時,須要重啓做業才能生效,後續但願能作到規則的動態熱加載。