Quantcast
Channel: WFU BLOG
Viewing all articles
Browse latest Browse all 784

Google Apps Script 如何進行加密解密+取雜湊值(hash)﹍MD5 SHA HMAC RSA AES 操作說明

$
0
0
使用「Google 試算表做為資料庫」後,除了需要瞭解「資料庫防駭技巧」,為了安全性著想,一些需要加密的運算不可在前端執行(Javascript會被看光)。 舉例來說,前端發送修改資料的請求時,後端必須先驗證這個請求的身份、權限,也就是檢查前端附上的 token 通行金鑰。這個金鑰當然不可由前端產生,否則 JS 演算邏輯被看到,就可任意偽造通行金鑰。 那麼 Google 試算表的後端語言 Google Apps Script(簡稱 GAS),是否有足夠安全的加密演算法、是否需要安裝外掛?請見本篇的說明 (圖片出處: pexels.com)

一、GAS 內建工具

前端向後端發送請求時,反應速度是非常重要的,如果 GAS 在後端進行加密時還要讀取外掛,導致伺服器回應請求的時間過長,那麼前端的使用者體驗一定不佳,訪客遲早跑光光。 值得慶幸的是找了很久,終於發現 GAS 官方提供了內建的各種知名加密演算法,並提供了範例說明,請參考這個頁面有詳細列表: 1. 工具介紹從官網頁面的函數列表,我們看到 GAS 提供了這些工具:
  • base64:將字串轉換為 64 位元編碼,只使用英數符號。如果使用了英數以外的字串例如特殊符號、中文,最好先經 base64 編碼處理
  • Digest 演算法:包含了 MD5、SHA 等雜湊演算法
  • Mac 演算法:一樣使用 MD5、SHA 等雜湊演算法,但需要提供加密金鑰
  • RSA 演算法:RSA 是一種非對稱加密演算法,需要公鑰與私鑰
2. 加密演算法概念如果不了解各種加密演算法的話,建議先閱讀以下參考文章建立基本概念: 3. MD5、SHA 雜湊值從參考文章我們可瞭解到,MD5、SHA 這類演算法可計算出「雜湊值」(hash),這與「加密」的概念不同,因為「加密」代表可以「解密」,而「雜湊值」是不可逆的,意思就是說 MD5、SHA 這類演算產生的字串無法解密及還原。 用比喻來說的話,MD5、SHA 演算法是產生供辨識身份的 "指紋"、"簽名",這是獨一無二難以偽造的,實作上多用於產生驗證碼、檢查碼之用。如果只是從後端產生 token 通行金鑰的話,雜湊演算法就已足夠,不一定要用到「加密」功能。 官網可看到 DigestAlgorithm 提供了這些演算法:MD2、MD5、SHA-1、SHA256、SHA384、SHA512。 而參考文章除了說明以安全性而言,越後面的演算法越安全(雜湊字串位元數越高),另外提到 2017 年 Google 已經破解了 SHA-1 演算法,所以現今的年代建議使用 SHA256 以後的演算法。 4. MAC 雜湊值根據參考文章,MAC、HMAC 演算法一樣是產生「雜湊值」(不可逆),但不一樣的地方是需要提供「加密金鑰」(key) 才行。 使用金鑰的好處是,進行通訊的兩方都擁有 key,就能進行傳輸不加密的明文,並附上「雜湊字串」做為檢驗碼,收到的一方同時檢驗明文與檢驗碼,就能知道傳輸過程是否被攔截與竄改。 如果同樣一件事使用 MD5、SHA 做為檢驗碼,那麼攔截者修改內容後再附上新的檢驗碼,收到訊息的人就檢驗不出是否被竄改了。 5. RSA 加密「對稱加密」演算法使用共同的金鑰(key)來加密與解密,RSA 為「不對稱加密」演算法,需分別產生 "公開金鑰" 與 "私密金鑰"。 傳輸訊息方將訊息使用 "公開金鑰" 加密,收到訊息方擁有 "私密金鑰" 即可解密,還原原始訊息。 「不對稱加密」演算法耗費 CPU 計算的時間比較久,不過是最安全的加密方式。

二、MD5、SHA 操作

官方說明書大概是寫給工程師看的,沒有一定的基礎知識八成看不懂這些函數要如何運作。 MD5、SHA 函數及使用參數如下: computeDigest(algorithm, value, charset)
  • algorithm 填入演算法名稱,參照官網「DigestAlgorithm
  • value 填入原始字串
  • charset 為編碼方式,參照官網「Charset
但是 computeDigest 函數回傳的雜湊值為陣列型態,實務上需要轉換為文字才能使用。一種作法是參考外國網友寫的轉換字串程式「MD5 function for GAS」,而我的作法是利用官方提供的 base64 編碼也可轉成英數字串。 下面程式碼以 SHA_256 演算法舉例: var sourceStr = "Blogger 調校資料庫", // 原始字串 含 UTF8 中文字串 SHA_256_hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, sourceStr), // SHA_256 雜湊值 非 UTF8 編碼 SHA_256_hash_utf8 = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, sourceStr, Utilities.Charset.UTF_8), // SHA_256 雜湊值 UTF8 編碼 base64_SHA_256_hash = Utilities.base64Encode(SHA_256_hash), // SHA_256 雜湊值 非 UTF8 編碼 經 base64 編碼轉為英數字串 base64_SHA_256_hash_utf8 = Utilities.base64Encode(SHA_256_hash_utf8); // SHA_256 雜湊值 UTF8 編碼 經 base64 編碼轉為英數字串 console.log(SHA_256_hash); // [ -70, 36, 121, -47, -80, -76, -98, -14, 97, 106, 15, -57, 44, -122, -69, 58, -1, -128, -126, 39, -27, 17, 86, 42, 16, 23, -34, 92, 64, 43, -11, -10 ] console.log(SHA_256_hash_utf8); // [ -66, -35, 9, -38, 15, -43, 107, 103, -87, 17, 54, -104, -117, -29, 76, -27, 42, -108, -60, 9, -61, -14, 55, -80, 6, 0, 46, 16, 18, -74, 6, -86 ] console.log(base64_SHA_256_hash); // uiR50bC0nvJhag/HLIa7Ov+AgiflEVYqEBfeXEAr9fY= console.log(base64_SHA_256_hash_utf8); // vt0J2g/Va2epETaYi+NM5SqUxAnD8jewBgAuEBK2Bqo=

三、MAC 操作

使用 MAC 演算法產生雜湊值的 MD5、SHA 函數及使用參數如下: computeHmacSignature(algorithm, value, key, charset)
  • algorithm 填入演算法名稱,參照官網「MacAlgorithm
  • value 填入原始字串
  • key 填入加密私鑰
  • charset 為編碼方式
以下程式碼以 HMAC_SHA_384 演算法舉例: var sourceStr = "Blogger 調校資料庫", // 原始字串 含 UTF8 中文字串 key = "WFU BLOG", // 私鑰 HMAC_SHA_384_hash_utf8 = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_384, sourceStr, key, Utilities.Charset.US_ASCII), // SHA_384 雜湊值 UTF8 編碼 base64_SHA_384_hash_utf8 = Utilities.base64Encode(HMAC_SHA_384_hash_utf8); // SHA_256 雜湊值 UTF8 編碼 經 base64 編碼轉為英數字串 console.log(HMAC_SHA_384_hash_utf8); // [ 4, -36, -68, 59, 82, -127, -13, 111, -28, -52, 65, 7, -72, 109, -38, 107, -83, -36, -39, -60, 72, 80, 68, -37, -12, 62, -45, -45, 33, -89, -9, 67, 63, -57, -29, -128, 53, 42, 94, -26, -21, 111, 72, -66, 112, -20, 27, 69 ] console.log(base64_SHA_384_hash_utf8); // BNy8O1KB82/kzEEHuG3aa63c2cRIUETb9D7T0yGn90M/x+OANSpe5utvSL5w7BtF

四、RSA 操作

GAS 提供的 RSA 加密函數研究一陣子才發現沒有用處,無法生成公鑰、私鑰(要自己想辦法取得),只能產生雜湊值,不能加密也無法解密。 仔細想了一下,難怪函數名稱幾乎都有 "Signature",原來 GAS 本篇提供的工具作用就是製作 "簽名"。既是如此的話,不如直接使用前兩個方案就好,不需特別操作更複雜的 RSA 方案。不過既然都花時間了,還是把研究結果貼一下。 computeRsaSha256Signature(value, key)以下為範例程式碼: var sourceStr = "Blogger 調校資料庫", // 原始字串 privateKey = "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAxiiQ5NEDMH3RRd1+gp5yDnT1r1cnVTQumaC1n6uK9JcHrDlfMASbMPO0adz1kMBN3jcMY3dRRcT4SoEkNxW1oQIDAQABAkBRNtcLsM5J1YcrxzfOePnuCumWz3WDajufI0rNAhWVYIbtSfgNQj0eZtMa2xshapGu7F7ov4UFb2YOlRhGl9xxAiEA/vnwYUZu4WmDbsemHTcxnpn6ivVs6bnzVDLx2rR1lZMCIQDG9Dr82jMAQ3SHCake6WGrU1oScP27BxtM9fFaGNTIewIhALnuLJnisIxzUsJ/l+SKEQbkpcya91bgoE3v8GlZWr09AiBIJQZC7Ijz/oIB+dHXAMBHFTmjWE/aA1C2DrVPe5OlgwIhALPUSYLt194IO89G79FCzCu+Cn3IxL0Xmxq7kzaCFERd", // 私鑰 從線上工具產生 privateKeyStr = "-----BEGIN PRIVATE KEY-----\n" + privateKey + "\n-----END PRIVATE KEY-----\n", // 完整私鑰字串 RsaSha256_hash = Utilities.computeRsaSha256Signature(sourceStr, privateKeyStr), // Rsa Sha256 雜湊值 base64_RsaSha256_hash = Utilities.base64Encode(RsaSha256_hash); // Rsa Sha256 雜湊值 經 base64 編碼轉為英數字串 console.log(base64_RsaSha256_hash); // MGnoHZQu2ZZFPACoMZMwMEC3dwUsc//EINixWsTjasydxc12nCIiDmkRpoF0G3O3ZRik69wSIgRurwjjjhNJ1A==

五、AES 加密、解密

AES 為知名的「對稱加密」演算法,介紹說明可參考維基「進階加密標準」。如果在 GAS 有加密、解密需求的話,因官方未提供內建工具,可參考以下的作法。 1. AES 工具這個網頁「CryptoJS libraries for Google Apps Script」將知名的 CryptoJS 函式庫搬到 GAS 使用,此頁面有操作說明,同時完整的程式碼放在這個頁面: 進入上面這個 GAS 頁面後,將「AES.gs」的所有程式碼複製到自己的 GAS,就可以進行操作了。 2. 範例程式碼以下的範例請自行修改原始字串、加密金鑰,就能進行加密與解密了: var sourceStr = "Blogger 調校資料庫", // 原始字串 使用 UTF8 編碼 Key = "WFU BLOG", // 加密金鑰 encodeStr = CryptoJS.AES.encrypt (sourceStr, Key).toString(), // 加密字串 decodeStr = CryptoJS.AES.decrypt (encodeStr, Key).toString(CryptoJS.enc.Utf8); // 解密字串 還原 UTF8 編碼 console.log(encodeStr); // U2FsdGVkX186XFuduXNEWWWUY02bhb+2IVKYyGcOoSg7dtmXxugjfTlGQ4TKC8qJ console.log(decodeStr); // Blogger 調校資料庫

六、補充說明

本篇使用到 GAS 官方提供的 base64 編碼工具,效果等同於前端 JS 的 btoa() 函數。然而官方提供的 base64 解碼工具跟 atob() 截然不同,解出來的不是字串,而是一個陣列,很難想像這是什麼東西,詳情可參考官網說明文件「base64Decode」:"Decodes a base-64 encoded string into a UTF-8 byte array"。 要還原 base64Encode() 字串的話不可直接用 base64Decode(),務必參照官方提供的範例程式碼,先解碼成陣列再製作出一個 blob 物件,再轉成字串才能成功: Utilities.newBlob(Utilities.base64Decode("這裡是 base64 編碼字串")).getDataAsString()
更多 Google Apps Script 相關技巧:

Viewing all articles
Browse latest Browse all 784

Trending Articles