大家下午好。我叫內森克拉克,使用 JMP 已經 22 年了,我來自 IDEXX 實驗室。我將與我的搭檔薩奇·達林一起為大家講解密鑰管理。
大家好,我是Sage,我和Nathan都在IDEXX工作。我在這裡是資深分析工程師,今天我們要跟大家聊一些秘密。
太好了。我們先來簡單聊聊什麼是密鑰?通常來說,至少從程式碼的角度來看,金鑰是一種憑證,是你的程式運作所必需的,這樣你才能存取某些東西,但同時,其他人不應該知道它。它和你登入電腦的密碼非常相似。你需要經常使用它,但你不應該把它告訴其他人。
金鑰是不同平台開發的一種機制,允許憑證以一旦發布就無法被查看的方式存在。因此,在後台任務中使用它們會更加安全。 JMP 本身並不具備這種功能。但是,隨著我們在 JSL 中越來越多地使用程式碼和後台功能,我們希望能夠擁有這種能力,以便與他人分享我們的工作成果,存取不同的資源,同時避免意外地將關鍵資訊洩露給任何人。
不久前,我和 JMP 支援團隊討論過,他們給了我一小段程式碼,我相信是 Evan McCorkle 寫的。這段程式碼可以掩蓋這些秘密,使其像我們在這裡看到的那樣存在,但你可以加密腳本並使用它。
它的基本原理是使用一個包含金鑰的函數,但需要一個輔助金鑰或存取短語(你知道它的存在),加密程式碼中的所有函數都包含這個輔助金鑰或存取短語,但其他人無法知道它的具體內容。即使他們知道使用密鑰 A 是他們想要調用的函數,或者類似的信息,他們也無法在不知道這個訪問短語的情況下直接訪問這些密鑰。加密之後,你可以將其作為秘密腳本包含在內,這樣你就可以在不知情的情況下獲取這些資訊。到目前為止,當我與 JMP 支援團隊溝通時,儘管他們嘗試過,但在測試中仍然找不到暴露金鑰的方法。我自己也在嘗試,但同樣沒有成功。這似乎是一個非常不錯的框架,可以用來建立你想要的金鑰安全儲存方式,同時又能允許使用者利用這些金鑰執行各種程式碼操作。
今天我們有兩個例子。我將以 Snowflake 為例,來講解我們如何實現這個功能。我這裡有一個 Snowflake 連接模板。這基本上就是我寫的函數,其中所有重要的部分都隱藏起來了。
我在這裡稍微放大一下。你看,我有一個 `get_secrets` 函數,它儲存著金鑰。就我目前的需求而言,我只需要使用者名稱和密碼就能登入 Snowflake。這是一個相對簡單的密碼陣列。
但我還有其他輔助功能可以利用它們。第一個功能會建立一個與資料庫的實際連接,並傳回新的資料連接器。這是一個需要你組合起來的字串。它包含了數據連接器,並連接到所有特定於你公司的組件。你只需要安裝 Snowflake 驅動程式。這是你從 Snowflake 網站安裝到電腦上的 Snowflake 驅動程式的名稱。這樣你就可以建立 ODBC 連線。你的資料庫是特定於你公司的。但是,你的公司伺服器連接到 Snowflake,等等。然後,它會從我們在這裡獲取的另一個數組中獲取密碼。它會將密碼放入其中,然後你就可以連接其他元件了。
我提供的最後一個函數更進一步。它接收你要執行的查詢字串,並在此函數中完成所有連接,然後執行查詢。最後,它會清除所有與該表相關的表腳本。我會在範例中解釋它的用途。但簡而言之,它提供了另一層保護,防止憑證意外共享或洩漏。
接下來,我舉一個非常簡單的例子。在第一個例子中,它基本上是執行一個原始查詢,我編寫了自訂 SQL,並插入了查詢語句的字串版本。運行這段程式碼後,我會從 Snowflake 取得一個表。它看起來很像 Big Class,因為它就是。我只是把它的副本上傳到了 Snowflake。這樣就能得到你想要的手錶。它包含了一些來源資訊。這很有用。我注意到,這樣做會封鎖使用者名稱和密碼,這也在我的預期之內。不過,我在嘗試各種方法時也注意到,有幾個例子(不管是不是偶然現象)密碼沒有洩露,但用戶 ID 卻洩露了。根據你對這些憑證的監控程度,我寫了一個額外的函數來移除這些資訊。
如果我註解掉這部分程式碼,現在運行它,它會建立一個新表,並使用另一個函數來執行查詢,結果是一樣的。只不過現在這個表裡沒有任何表腳本了。這很好。為了更詳細地解釋它的工作原理,例如,如果我想刪除這個表,只選擇星形圖及其所有列,現在應該可以正常工作,並將它們全部提取出來。如果想新增一些篩選功能,也可以在這裡操作。它的運作方式與標準 SQL 語言執行這些查詢的方式完全相同。你只需將字串傳遞給它,就能得到清理後的資料表。這是一個相對簡單的例子。現在我想把這個問題交給 Sage,他們有一個更複雜的版本,可以結合你的函數分層來實現。我現在就交給 Sage 了。
FoxDrive 是一個線上資料夾,你可以在這裡儲存資訊。我們用它來做很多不同的事情,所以我首先想解決的是如何透過 Box 進行身份驗證,以便將檔案從 Box 拉取到我的本機電腦。這需要透過 Box Developers 建立一個應用程式來進行身份驗證。然後,這個應用程式需要提供一個叫做 JWT(JSON Web Token)的東西,這個 JWT 可以用來取得存取令牌,然後我才能使用它來存取 Box。我的情況有點複雜,因為我以前從未接觸過 JSON 或 JWT,所以最終我必須自己實現它。
正如 Nathan 所描述的,右側是我的 secrets.jsl 檔案範例。目前它尚未加密,因此您可以查看其內部結構。我的文件也以一個獲取密鑰的函數開頭。這裡存放著我的所有 JWT 資訊。 JWT 包含客戶端 ID、客戶端金鑰等應用程式特有的資訊。我將這些資訊放入一個關聯數組中,以便提取密鑰。然後,為了存取這些信息,我編寫了其他函數,這些函數從客戶端 ID 開始,依次存取關聯數組中的各個部分。
我只展示其中一個例子,因為如果全部展示,你會看到關聯數組的所有組成部分,那可能會很無聊。這是一個獲取客戶端 ID 的範例。在這個例子中,我使用金鑰來取得另一個金鑰,也就是我的 Box 存取金鑰,然後透過這個金鑰取得客戶端 ID。再次強調,為了確保所有資訊都經過加密,以便從同一個函數中提取,所有操作都嚴格保密。
我需要補充一點,這與獲取 Box JWT 令牌有關。這需要一些技巧,因為 JWT 需要斷言才能取得新的存取令牌,而存取權杖又需要簽章元件。由於簽名組件本身就是敏感訊息,所以它被視為敏感資訊。我的 secrets.jsl 檔案包含一些函數,用於進一步處理從 Box 獲取這些資訊。在這個例子中,我的 JWT 函數…我把它封裝起來,以便於理解。我的 JWT 函數會向 Box 建立斷言,以便取得存取權杖所需的資料。為此,我必須取得上面 secrets 檔案中包含的明確訊息,包括我的 Box 企業 ID、Box 用戶端 ID、Box 公鑰、私鑰以及我的應用程式獨有的金鑰。
接下來,我找到了一些故障排除方面的資料,例如密鑰缺失時需要注意的事項,以及真正重要的部分。為了進行 JWT 斷言,我實際上必須使用 Python 來實作。這也包括另一個在 secrets.jsl 中使用其他語言的例子,這也是我需要處理的問題,以確保某些內容可以列印出來。這就是為什麼 JWT 資訊要放在 secrets.jsl 中,而不是直接放在我用來存取這些資訊的腳本中,這樣才能確保資訊被加密。這也是一個難得的機會,可以看到 Python 如何整合到 JSL 中,這非常酷。
然後我將這些資訊傳送給 Python 腳本,腳本會取得該 JWT 的特定簽名,並據此建立憑證,最後傳回 JWT 斷言,以便我後續使用。有了 JWT 斷言,我就可以獲得存取權杖了。接下來,我取得 Box 的 JWT 令牌(如上圖),然後使用 client_ID 和 client_secret,透過 HTTP 請求將 JWT 斷言、client_ID 和 client_secret 傳送到 Box,從而建立一個新的存取權令牌。
這裡還有一些空氣捕捉功能,供有興趣深入挖掘的人使用,但它會發送我是否真正獲得了訪問令牌的信息,然後將訪問令牌返回給用戶。
它在哪裡使用?如何使用?我將展示一個範例,因為關於存取權杖本身,我們可以展開一些有趣的討論。為了整合它,我創建了一個通用資料夾作為範例,但在這裡,我的實際程式碼只展示一些輸入。
這裡我加入了 Box 連線(在我的情況下是加密的),因為在處理完金鑰檔案後,你需要這樣做。 Box 會為資料夾分配 ID 程式碼,這樣就能唯一知道你要搜尋哪個資料夾裡的檔案。你需要在這裡輸入你唯一的資料夾 ID。我這裡只提取 JMP 檔案。根據你的需求,這可能更複雜,但我的範例只涉及 JMP 檔案。
我還在這裡添加了一個參數,可以用來查看遞歸深度。 Box 很有意思,因為它採用的是物件導向的方式。你有一個資料夾 ID,然後你可以在該資料夾 ID 下找到子資料夾 ID,所以你必須遞歸地搜尋這些資料夾。我允許我的腳本遞歸到我認為合適的層數,在這個例子中是五層。然後我指定了下載檔案的位置,以及檔案的命名方式。在這個例子中,在講解完它的工作原理以及出於安全考慮的訪問令牌的一些細節之後,我將展示我的範例:我將從 Box 中提取一些 JMP 檔案。
這裡面有一些關於遞歸搜尋 JSL 的函數。我不會詳細介紹這些函數或 Python 下載,但我會專注於講解 Box 的主要連接。我們之前討論過一些安全方面的問題,例如每個公司/每個人應該把這個連接放在哪裡比較合適,這個問題可能有點意思。放置位置可能略有不同,安全性也取決於你討論的是哪個位置,但就我而言,我把它放在這裡了。首先,這個主函數需要從 Box 取得資訊。為此,我必須獲取我的訪問令牌。取得存取令牌是之前你看到的加密 secrets.jsl 檔案的一部分。在這個例子中,它會傳回這個訪問令牌。這是一個存在於 JSL 檔案中的變數。
在這個 JSL 程式碼中,因為我在這裡取得了存取令牌,如果我在第 583 行新增一個列印語句,你就能看到這個存取令牌。 Box 的一個優點(或者說它的一個優點)是,這些訪問令牌會過期。實際上,幾乎不存在有人以後還能使用它們的風險。雖然風險已經降低,但如果你列印這個存取令牌,或者取得腳本並運行它,然後在該位置列印,仍然存在潛在的安全風險。
理想情況下,你可以這樣做:與其像我這樣把 Box 存取權杖放在這裡,不如把身份驗證部分(包括測試和搜尋)移到 secret.jsl 檔案中,然後進行加密。這樣一來,令牌就無法訪問,其他人也無法查看。這是一個略有不同的使用場景,你可以根據自己的喜好選擇合適的實作方式。最安全的方式是將 Box 存取令牌放在 secrets.jsl 檔案中並進行加密,這樣任何人都無法查看。不過,這裡我先這樣處理,這樣就好,我們可以就此展開討論。
然後它會獲取 Box 訪問令牌,之後我就可以搜索 Box 訪問文件了,因為我已經有了身份驗證信息,證明我的身份,所以應該允許我訪問。我這裡有一個演示,演示中我從 Box 驅動器上的一個文件中提取資料。這個檔案區域看起來像這樣:Secrets_JMP Discovery、File Examples_Secrets,我有一些 JMP 表要從這個位置提取並連接。為了實現這一點,我要執行這個腳本。接下來會播放一段令人沮喪的等待音樂,因為運行腳本有時會出現這種情況,需要一些時間。
身份驗證,告訴我我是誰,告訴我你是誰。好了,這就是我拿到的文件。你可以看到我的來源表,那些是我在那個 Box 位置的 JMP 表的名稱,這些是連接在一起的表。只是一些有趣的玩笑。
這就是我處理 Box 存取權杖並將其與 JWT 整合的方法。我打算把它轉交給 Nathan。
鑑於 Sage 和我展示的所有內容,我們該如何真正地將其付諸實踐,並找到一種方法,讓更多人(而不僅僅是我和 Sage)能夠參與到這項工作中來呢?我們已經將程式碼上傳到了 GitHub。這裡是 GitHub 的連結。我們已經創建了代碼倉庫。它也與簡報相關聯,並包含我們的範例。
現在,根據你想要如何組織,這裡有很多選擇。但我們主要整理的是密鑰模板。這就是我之前展示的包含基本值的範本。然後我們這裡有一些不同的範例。在這些範例中,我們基本上有三個主要文件,這是我們展示的包含所有資訊的範本。你可以在這裡填寫你自己的個人或公司信息,以及你進行加密的操作。為了確保安全,我們建議你將此範本儲存到你的程式碼倉庫或複製的倉庫中,並將範本替換為「raw」。這樣,所有真正重要的資訊都會被隱藏起來。我們在 .gitignore 檔案中忽略了 raw.jsl 檔案。這樣,這些資訊就保留在本地供你使用。你的憑據也保存在這裡。然後對原始文件進行加密,並將加密後的文件版本上傳。
現在,GitHub 上應該只保留你可用的加密版本。每個資料夾裡都有相應的文檔,說明其具體用法。你想在主 README 文件或文件中分享多少信息,完全取決於你想讓用戶了解如何使用這些信息。如果你有類似 Project Jab 這樣的專案(我和 Sage 去年介紹過),它提供了一種建立插件的方法,允許你從各個倉庫拉取檔案。這樣,你就可以拉取所需的金鑰檔案來完成你的任務。這是一種既能保證資訊的可用性,又能兼顧不同程度保密性的好方法。
正如 Sage 所暗示的那樣,如果你的程式碼變得越來越複雜,為了保持這種程度的保密性,你可能會陷入這樣的境地:最終你會將越來越多的信息放入這個加密文件中。
就像 Sage 在她的模板裡提到的那樣,她有很多函數可以完成很多工作,但當她舉例說明時,她特別指出了那個特定的訪問令牌。我記不清它在函數裡的具體位置了,但如果你不想讓其他人在主要示例中看到它,你可以把這個函數添加到你的模板或模板化的原始文件中,然後加密它,這樣就完全隱藏起來了。你需要處理的層數取決於你的特定用例和你想要實現的目標。在 Snowflake 中,這相對容易,因為我可以直接使用連接或簡單地清理表。所以實際情況可能會有所不同,取決於你需要連接什麼,例如 API 之類的。
但我認為這是一種相對容易理解和利用的方式,可以梳理你的秘密,並為那些想要改進其背景編碼的人提供多層次的指導,同時保持可靠的網路安全實踐,以保持一切乾淨整潔。
我想今天我們要討論的內容就到此為止了。非常感謝JMP的各位給我們這次機會,也感謝所有觀看的觀眾。
完全同意。希望你守住了秘密。如果你願意,今年可以來JMP Discovery找我們,我們會在那裡。
是的,我們會的。謝謝。