hash - 有沒有一般被認為值得信賴的 SHA-256 JavaScript 實現?
我正在為一個論壇撰寫登入功能,並且需要在將密碼傳送到伺服器之前,先在客戶端使用 JavaScript 對密碼進行雜湊。我在找出哪個 SHA-256 實現是可以信任的時候遇到了麻煩。我原本以為會有某種大家都在使用的權威腳本,但我發現有很多不同的專案,每個專案都有自己的實現方式。
我明白,使用其他人的加密貨幣總是一種信任的飛躍,除非你有資格自己審查,而且“值得信賴”並沒有普遍的定義,但這似乎是一些常見且重要的事情,應該有某種關於使用什麼的共識。我只是太天真了嗎?
編輯,因為這在評論中經常出現:是的,我們在伺服器端會再次進行更嚴格的哈希。客戶端的哈希不是我們存入資料庫的最終結果。客戶端的哈希是因為使用者端要求這麼做。他們沒有給出具體的理由,可能只是他們喜歡做到過度安全。
答案#1
在 https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest 我發現了這個使用內部 js 模組的程式片段:
async function sha256(message) {
// encode as UTF-8
const msgBuffer = new TextEncoder().encode(message);
// hash the message
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer));
// convert bytes to hex string
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex;
}
請注意,crypto.subtle 只有在 https 或 localhost 上可用——例如,對於本地開發使用 python3 -m http.server,你需要在你的 /etc/hosts 中添加這一行:0.0.0.0 localhost
重啟 - 然後你可以打開 localhost:8000 並使用可運作的 crypto.subtle。
答案#2
大多數現代瀏覽器現在原生支援加密操作。請參見 https://developer.mozilla.org/en-US/docs/Web/API/Crypto。
也就是說,用戶端加密通常沒有看起來那麼好。
基本原則是,你(在伺服器端)必須假設客戶端發生的一切行為都可能是惡意的,甚至可能根本沒有運行你的程式碼。如果你的加密操作旨在保護使用者的資料免於第三方的發現或干擾,那麼應該使用 TLS 作為適當的加密層,而不是在客戶端自己實作加密。
透過 TLS 傳送敏感資料不需要額外的加密來遵守任何法律或最佳實務,除非這些加密已由 TLS 透明地提供。如果你要傳送敏感資料,請透過 TLS 傳送。從你的應用程式的角度來看,這將看起來像純文字,但這完全合理,僅僅反映了加密的抽象層級,而不是它提供的安全等級。僅僅因為一個安全機制使用起來感覺簡單,並不意味著它提供的保護是簡單的。
不要說:「我已經在使用 TLS,但我還要通過客戶端實現來讓它更好。」 用更多與安全相關的組件來複雜化你的應用程式並不會增加安全性。相反地,你是在犧牲真正的長期安全性,只為了感覺自己做得更多。
此問題所指定的情境恰好是進行客戶端加密的錯誤場所。這個問題所建議的做法之所以很少被大型科技公司採用,是有充分理由的,而且他們成功抵禦了國家級攻擊者。
客戶端瀏覽器加密適用的情況 是當客戶端被視為獨立運行,且用戶的保護不指望由伺服器運營者提供的時候。這是一個極為罕見的情況,但會出現在像離線網頁應用這樣的情境中。
答案#3
對於有興趣的人,這是使用 sjcl 創建 SHA-256 哈希的程式碼:
import sjcl from 'sjcl'
const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
答案#4
Forge 的 SHA-256 實現既快速又可靠。
要對多個 SHA-256 JavaScript 實現進行測試,請訪問 http://brillout.github.io/test-javascript-hash-implementations/。
我機器上的結果顯示,forge 是最快的實現,而且比在被接受答案中提到的 Stanford Javascript 加密庫(sjcl)快得多。
Forge 的大小為 256 KB,但提取與 SHA-256 相關的代碼後,大小減少到 4.5 KB,請參見 https://github.com/brillout/forge-sha256
答案#5
不,沒有辦法使用瀏覽器的 JavaScript 來提高密碼安全性。我強烈建議你閱讀這篇文章。在你的情況下,最大的问题是先有雞還是先有蛋的問題:
在提供 JavaScript 加密時,什麼是「先有雞還是先有蛋的問題」?
如果你不信任網路能傳送密碼,或者更糟的是,不信任伺服器不會保留使用者的秘密,你就無法信任它們能傳送安全碼。同一個在你引入加密之前偷看密碼或閱讀日記的攻擊者,在你引入加密之後,只是轉而劫持加密程式碼。
[...]
為什麼我不能使用 TLS/SSL 來傳送 Javascript 加密程式碼?
你可以。這比聽起來要困難,但你可以使用 SSL 安全地將 Javascript 加密傳送到瀏覽器。問題是,建立了 SSL 安全通道後,你就不再需要 Javascript 加密了;你擁有的是「真正的」加密。
這導致了這個:
在 Javascript 中運行加密程式碼的問題在於,幾乎任何加密所依賴的函數都可能被用來構建宿主頁面的任何內容悄悄覆蓋。加密安全性可能在早期過程中被破壞(例如生成偽隨機數,或篡改算法使用的常數和參數),也可能在後期被破壞(例如將金鑰資料偷偷傳回給攻擊者),或者 --- 在最可能的情況下 --- 完全繞過加密。
沒有可靠的方法可以讓任何一段 Javascript 代碼驗證其執行環境。Javascript 加密代碼不能詢問,「我真的是在使用隨機數生成器,還是正在使用攻擊者提供的某種仿製品?」更不可能聲明「除了我這個作者批准的方式,任何人都不允許對這個加密秘密進行任何操作」。這兩個屬性通常在使用加密的其他環境中可以提供,而在 Javascript 中是不可能的。
基本上的問題是這個:
- 你的客戶不信任你的伺服器,所以他們想要添加額外的安全代碼。
- 那個安全碼是由你的伺服器傳送的(就是那些他們不信任的伺服器)。
或者,
- 你的客戶不信任 SSL,所以他們希望你使用額外的安全碼。
- 那個安全碼是透過 SSL 傳送的。
注意:此外,SHA-256 並不適合用於此,因為對於未加鹽且未迭代的密碼,暴力破解非常容易。如果你決定仍要這麼做,請尋找 bcrypt、scrypt 或 PBKDF2 的實現。
答案#6
我發現這個實現非常容易使用。同時也有寬鬆的 BSD 風格許可證:
jsSHA: https://github.com/Caligatio/jsSHA
我需要一個快速的方法來獲取 SHA-256 雜湊的十六進位字串表示。只用了三行代碼:
var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
