天天看點

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

作者:前端進階

大家好,很高興又見面了,我是"前端‬進階‬",由我帶着大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發!

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

僞随機數:用确定性的算法計算出來自[0,1]均勻分布的随機數序列

1.什麼是僞随機數?

僞随機數是用确定性的算法計算出來自[0,1]均勻分布的随機數序列。并不真正的随機,但具有類似于随機數的統計特征,如均勻性、獨立性等。在計算僞随機數時,若使用的種子不變,那麼僞随機數的數序也不變。

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

什麼是僞随機數?

僞随機數可以用計算機大量生成,在模拟研究中為了提高模拟效率,一般采用僞随機數代替真正的随機數。模拟中使用的一般是循環周期極長并能通過随機數檢驗的僞随機數,以保證計算結果的随機性。

2.前端如何生成僞随機數?

以熟悉的Math.random()為例,該函數其實并不随機,它是一個僞随機數生成器(Pseudo Random Number Generator,簡稱PRNG),當指定同一個random_seed啟動時,它生成的随機數序列是一樣的!比如下面的例子将生成同樣的随機數序列:

// 示例代碼來源:[V8 Deep Dives] Random Thoughts on Math.random(),https://dev.to/puzpuzpuz/v8-deep-dives-random-thoughts-on-math-random-2ci4
node --random_seed=42
Welcome to Node.js v14.17.3.
Type ".help" for more information.
> Math.random()
0.5254990606499601
> Math.random()
0.963056226312738

node --random_seed=42
Welcome to Node.js v14.17.3.
Type ".help" for more information.
> Math.random()
0.5254990606499601
> Math.random()
0.963056226312738           

是以,隻要擷取random_seed,就能預測Math.random()所傳回的"随機序列",這是很可怕的事情。

雖然,擷取random_seed并不是一件簡單的事情,不過并非完全沒有可能,因為random_seed不是随機的,而是依賴于一些内部狀态,比如浏覽器啟動時間、某個變量的虛拟記憶體位址,這些内部狀态是有迹可循。

2014年,Andriod版的Firefox就曾被人破解過Math.random()。

是以,在對安全性要求比較高的場景中,不要使用Math.random()。CVE中有多個安全漏洞是與Math.random()相關。

3.随機數的社群方案和浏覽器方案

3.1 随機數社群方案CryptoJS

為了滿足大家對安全加密API的需求,社群提供了很多解決方案,以CryptoJS最出名。

// 不同加密算法的調用方式
const hash = CryptoJS.MD5("Message"); 
const hash = CryptoJS.SHA1("Message"); 
const hash = CryptoJS.SHA256("Message"); 
const hash = CryptoJS.SHA512("Message"); 
const hash = CryptoJS.SHA3("Message");            

CryptoJS是标準、安全加密算法的 JavaScript 實作,是一個完善的标準、安全加密算法集合,使用最佳實踐和模式在 JavaScript 中實作。 速度快,并且具有一緻且簡單的界面。

或者使用社群的uuid方案:

import { v4 as uuidv4 } from 'uuid';
uuidv4(); 
// 輸出 '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'           

從NPM的下載下傳量來看UUID在前端更火爆,周下載下傳量達到了驚人的61556K:

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

UUID vs crytoJS前端使用對比

但是,對于加密這種CPU密集型應用,純JavaScript的方案存在比較嚴重的性能問題,且不夠安全。

3.2 随機數浏覽器方案Web Cryptography

2017年,W3C釋出了Web Cryptography API,提供更加安全、性能更好的加密API。其中,crypto.getRandomValues() 用于生成更加安全的随機數,它是密碼學安全僞随機數生成器(Cryptographically Secure Pseudo Random Number Generator,簡稱CSPRNG)。其實,CSPRNG也并非生成真正的随機數,隻是它通過一些嚴格的密碼學測試,可以認為是安全的。

crypto.randomUUID()是基于CSPRNG的,是以可以認為是安全的。其使用加密安全随機數生成器生成 v4 UUID。

Crypto.getRandomValues() 方法可以獲得加密強度高的随機值, 作為參數給出的數組填充了随機數(在密碼學意義上是随機的)。

為了保證足夠的性能,實作沒有使用真正的随機數生成器,而是僞随機數生成器,其種子值具有足夠的熵。

getRandomValues() 是 Crypto 接口中唯一可以在不安全的上下文中使用的成員。

4.getRandomValues和randomUUID

4.1 getRandomValues方法

getRandomValues方法的文法如下:

getRandomValues(typedArray)           
  • 參數typedArray:基于整數的 TypedArray,包含:Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、BigInt64Array、BigUint64Array(但不是 Float32Array 或 Float64Array)。 數組中的所有元素都将被随機數覆寫。
  • 方法傳回值:傳回數組與 typedArray 相同,但其内容已替換為新生成的随機數。 請注意,typedArray 是直接修改的而不是拷貝。

具體調用示例如下:

const array = new Uint32Array(10);
self.crypto.getRandomValues(array);
// 擷取getRandomValues方法
console.log("幸運數字:");
for (const num of array) {
  console.log(num);
}           

并非所有的浏覽器版本都支援這個方法,具體支援情況如下圖:

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

getRandomValues浏覽器支援情況

4.2 randomUUID

Crypto 接口的 randomUUID() 方法用于使用加密安全随機數生成器生成 v4 UUID。其調用文法如下:

randomUUID()           
  • 參數:無
  • 傳回值:包含随機生成的 36 個字元長的 v4 UUID 的字元串。

具體調用方式如下:

let uuid = self.crypto.randomUUID();
// 擷取randomUUID方法
console.log(uuid); 
// 傳回值類似"36b8f84d-df4e-4d49-b662-bcde71a8764f"
           

并非所有的浏覽器版本都支援這個方法,具體支援情況如下圖:

CryptoJS vs UUID ? 聊聊 Web Cryptography 終極方案?

randomUUID的浏覽器支援情況

4.3 注意事項

不要使用 getRandomValues() 來生成加密密鑰,如果确實要生成,可以使用 generateKey方法代替。

getRandomValues方法無法保證在安全上下文中運作

Web 密碼規範沒有規定最低熵程度。 相反,使用者代理被敦促在生成随機數時提供最好的熵,使用使用者代理本身内置的定義明确、高效的僞随機數生成器,但使用從僞随機數的外部來源擷取的值作為種子。例如 特定于平台的随機數函數、Unix /dev/urandom 裝置或其他随機或僞随機資料源。

參考資料

https://baike.baidu.com/item/%E4%BC%AA%E9%9A%8F%E6%9C%BA%E6%95%B0/104358?fr=aladdin

https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues

https://developer.aliyun.com/article/787589

https://caniuse.com/?search=crypto

https://cryptojs.gitbook.io/docs/