20190702 7/2 晚上10點整左右,收到用戶回報說網站怪怪的,說Browser 上顯示cloudfare cdn.js有問題

figure-1

當下有點緊張,但檢查機器的狀況都是正常,所以看起來是 CDN出事了

因為當下在外面,也沒辦法處理,就先回家再說

結果在途中同事轉文說Cloudfare掛了的大事件

還好我們的服務只有少部分的文章可能是 embed js 還依賴著cdn,其他大部分的服務都已經脫離clourefare

不然影響可不小 (應該就一起502 Bad Gateway )

當然怎麼大的服務出錯,總是要看看別人怎麼 面對酸文的 應該說,是怎麼去面對的

原文連結

花了點時間看完,覺的寫的非常有趣,非常的工程,除了說明問題外,也帶來不少有趣的資訊

這邊節錄把看完的心得及想法分享出來


從發現 到 應變處理


看完整篇,用時間線的方式把文章重新簡單整理一下: (有色字,是我的補充)

1. 13:42  (CST  21:42)  工程人員用Jira部署了一則防止XSS的防火牆正則表示式

> 部署只需要幾分鐘就可以到全球部署完成 (還有它們用jira) 

2. 13:45  (CST  21:45)  全球Cloudflare出現了大量的502 

> 當伺服器異常,會收到了alert通知,有做很好的報警系統

figure-2

3. 14:00   (CST  22:00)  他們發現不是攻擊或其他資安問題 

> 第一時間人員確認問題為何

4. 14:02   (CST  22:02)  負責人,決定關掉防火牆,但防火牆規則用了大量的100%. CPU ( 無法連進去關閉或重啟 )

之後找到了,其他的入口進入系統,關了防火牆,發現 CPU 佔有率就正常了

接著,在某個範圍內,先除掉最新加入的規則後,再重啟防火牆,也是正常

>  Trouble Shooting 

5. 14:52 (CST 10:52),最後修正後 (大概就把那條規則revert掉) 全球防火牆重啟, 整個事件大約就一小時左右結束

> 除掉規則,並修復它


全球大災害,只因一條 Regular Express 正則表示式 


沒錯,就是所有工程師都愛用,但用過就忘的Regex ( 反正比較復雜的我是每次用都得查表或用Reg101才能完成 )

只因為他們的工程師上了一條WAF 防火牆上的規則

看起來似乎是用 lua來寫的正則表示式,使用的是比較舊的PCRE 規則 

因為該表示式有嚴重的反復回溯運算,最後使得CPU資源耗盡

一部署下去,造成CPU爆炸

然後該規則又因為自動部暑的關係,快速的deploy的到全球的服務上

結果就...如下圖

figure-3


詳細問題及復現


而官方的詳細回應我覺的很棒,完整且詳細,但我說重點即可, 問題源頭為工程師所部署的規則如下:

(?:(?:\”|’|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))

而重要的side effect在於這塊:

.*(?:.*=.*)))

這個語法造成整個CPU 使用率的整個昇上來到100%

主要是這段正則會試著匹配任何字符串緊跟的字串。结果反復回溯,最後使得CPU資源耗盡

試著翻寫一下原文的說明如下:

如果是x=x 正则會跑23步;如果是x=xx, 正则會跑33步;如果是20個x,比如x=xxxxxxxxxxxxxxxxxxxx,就會跑555步

要是語法改成.*.*=.*去匹配比如foo=bar; 那就完了,需要 5,353步

figure-1

看了官方的說明後,文內表述:

最終原因為該正則表示式的演算法 為 非線性的時間複雜度而造成這個狀況

Ken Thompson在1968年就有一篇文章把這類型的轉為線性複雜度,但 老派的Perl正則達示式沒有更新就是了

原文真的對這規則解釋很多也蠻深入,想深究的人一定要去看看

另外,我嘗試用自已的電腦 跑Perl or Regex101 or PHP 試跑一下該 PERE 規則,其實無法重現該狀況,有機會再拿Lua 來試試
這邊推一下 Regex101的 Regex Debugger 模式,這是一個能一步一步拆解,  正則複雜度及步數的工具,如果覺的正則很複雜及不好拆解,可以善用這個工具,不過也一樣玩不出這個問題的非線性步數狀況


figure-4


思考,從這裡我學到什麼?


開發人員或Code Review 時,甚至 QA為何沒發現?

其實,這種Bug屬於效能上的問題,而Regular Express 在複雜度上又很難被抓出

若是放在其他功能上,被抓到漏洞,就是一種 ReDoS (Regular Expression Denial of Servic) 正規表示式阻斷服務攻擊

就是一直打,每打一次,你的CPU就會抖一下,多打幾次就掛了


而這次用在防火牆上規則,就擺明著每個request都會通過該規則,所以算是自爆 ( 就是自已ReDoS自已的概念 )

在CI/CD 的流程中,就算做了基本的單元測式或整合測式都很難發現這個問題,但若有加入效能的測試項目,或許會被抓出來或許能避開這個事件 ; 但也可能無法,畢竟線上的壓力指數跟你小小的單元測試效能有點差異 (就看詳細怎麼做)

另外也因太強的部署機制 ( 太快也不行... ) 造成這個事件在很短的時間內,就發佈到全球

有經驗的人也有指出,若服務有做 "灰度發佈測試" 

"就是先部署到一部分的城市或範圍,而非全部,例如可以先部署到一個小地區幾天,等穩定後,再全面部署"

至少能減少傷害範圍,災情就不會怎麼慘重

思考總結


雖然是災害,但該事件的因應及危機處理讓我學到不少東西,也讓我們能借鏡跟思考自家服務所不足的部分,下面是一個簡單的小結:

  • 正則表示式很危險,主要是很難分析其複雜度,用的不好,很容易造成資源上的風險,可以考慮像Google Re2,執行上會多一層保護
  • CI /CD 流程中,除了單元測試,還要處理效能或壓力測試
  • 部署太快我覺的不是缺點,只是少了灰度發佈測試,先小範圍的的發佈可以比較理想

最後,這是一個共筆發文的專案,我是 J 大,喜歡可以在下面按讚支持即可

拍手 拍手
35 次拍手
拍手 拍手
追蹤

推薦文章

您需要 後才能開始留言
還沒有人討論誒,快來搶沙發...
聲音節目
沒有描述
--:--
--:--
1.0x
播放速度
2.0x
1.75x
1.5x
1.25x
1.0x
0.75x
收藏節目
播放清單
沒有播放清單
沒有待播放的清單
返回播放器
接著播放
清除全部
沒有待播放的清單