題:
將鹽和哈希密碼存儲在同一字段中是否安全/明智?
PenumbraBrah
2018-04-06 19:26:27 UTC
view on stackexchange narkive permalink

在使用Argon2在我的應用程序中對密碼進行哈希處理時,我注意到它會生成這樣的字符串(例如,用於密碼“ rabbit”):

  $ argon2i $ v = 19 $ m = 65536,t = 3,p = 1 $ YOtX2 // 7NoD / owm8RZ8llw == $ fPn4sPgkFAuBJo3M3UzcGss3dJysxLJdPdvojRF20ZE =  

我的理解是在 p = 之前的所有內容都是參數, p = 的主體為鹽,最後一部分為哈希密碼。

可以將其存儲在SQL數據庫的一個字段中( varchar(99)),還是應該將字符串分成其組成部分?我應該將鹽與哈希密碼分開存儲,並將參數保留在代碼中嗎?

[非常相關](https://security.stackexchange.com/questions/17421/how-to-store-salt)
如果使用[_pepper_](https://security.stackexchange.com/questions/3272/password-hashing-add-salt-pepper-or-is-salt-enough),則可以將其存儲在數據庫外部。
PHP [password_hash](http://php.net/manual/en/function.password-hash.php)函數也使用散列字符串存儲鹽(例如:$ 2y $ 10 $ .vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1)
“ varchar(99)”似乎太緊了。如果下一版本的哈希庫添加參數或使哈希更長,該怎麼辦?您的數據庫是否沒有“ varchar(MAX)”或類似名稱?
七 答案:
Anders
2018-04-06 19:36:50 UTC
view on stackexchange narkive permalink

您應該將其存儲在單個字段中。

我知道這對於來自數據庫背景的人們來說似乎有點不直觀,在這種情況下,操作方法是規範化數據並且使用序列化字符串被認為是醜陋的。但是從安全實踐來看,這是很合理的。

為什麼?因為單個開發人員需要使用的最小單元就是該完整字符串。這就是應用程序需要傳遞給哈希庫的功能,以檢查所提供的密碼是否正確。從開發人員的角度來看,哈希庫可以是一個黑匣子,並且她不必擔心那些討厭的部分的實際含義。

這是一件好事,因為大多數開發人員都是不完善的人(我知道,因為我是我自己)。如果您讓他們將字符串分開,然後再嘗試將它們重新組合在一起,它們可能會搞砸,例如給所有用戶相同的鹽或完全不加鹽。

僅將參數存儲在代碼中也是一個壞主意。隨著處理器變得越來越快,您將來可能希望增加成本因素​​。在遷移階段,不同的密碼將具有不同的成本因素。因此,您將需要有關密碼級別的信息,以了解用於加密它的成本因素。

許多密碼庫將機密和鹽作為單獨的參數。僅從該角度來看,將摘要和鹽/熵存儲在單獨的字段中似乎是明智的。如果分開存放,則鹽的長度甚至不必均勻。您可以一次對算法進行版本控制並支持一個以上的算法,從而使您可以平穩地將用戶推向更新,更強大的算法,而無需更改密碼,等等。
@Craig許多較早的庫都這樣做,但是我想說現在它被認為是不好的設計。至於版本控制算法,您也可以在一列中使用完整字符串。
這就是為什麼我喜歡堆棧交換的原因。每天我學到一些我不知道的東西。
這不僅僅是從安全實踐的角度來看才有意義。考慮到您佈置的原因,從規範化的角度來看這仍然有意義。
這還具有防過時的優勢;如果您決定稍後更改哈希參數,則數據庫中的舊哈希將繼續_(只要您使用庫函數來比較哈希,就像您應該使用Argon2一樣,而不是直接比較哈希字符串,)_
@Anders您正在考慮使用哪個庫來支持使用單個密碼字段對存儲的密碼進行版本控制?我可以看到如何使它起作用,並且這種方法具有相當的魅力,但是我從未見過如此瘋狂。
@Voo任何庫。您需要的所有信息-算法,參數,鹽-都存儲在單個列中的字符串中。
-1
@Voo是的,重點是不要修改字符串。絕對不要那樣做。我不是故意建議。我認為我們彼此誤會了。
@Voo spring-security版本5朝著這個方向發展:https://docs.spring.io/spring-security/site/docs/current/reference/html/core-services.html#pe-dpe。我發現缺少的唯一功能是,該庫沒有給應用程序提供機會將哈希密碼字段遷移到默認編碼方案(如果不使用它)
對於那些正在尋找一個示例庫的示例,該示例庫將包括算法在內的所有參數透明地存儲在一個字符串中,並允許順利升級到更強的設置,請查看[PHP的password_ *函數](http://php.net/password),包括[password_needs_rehash](http://php.net/manual/en/function.password-needs-rehash.php)的示例。
@Voo此版本控制必須由開發人員觸發,但解碼和重新編碼完全由庫處理。
@Anders我對哪些庫實際上具有使用單個字符串而不是通常的三個字符串的API感興趣。畢竟,在這種情況下,您確實需要一些其他配置。Spring API非常有趣。
@Voo另一種將所有三個部分存儲為單個字符串的密碼加密實現是[bcrypt](https://en.wikipedia.org/wiki/Bcrypt),其中包含多種語言的實現。
Machavity
2018-04-06 22:34:26 UTC
view on stackexchange narkive permalink

您所缺少的是哈希處理原始數據,減去原始字符串。當您要針對哈希驗證字符串時,請使用提供的字符串以及原始哈希數據(成本,鹽等),然後生成一個新的哈希。如果新哈希與舊哈希匹配,則對該字符串進行驗證(換句話說,該字符串永遠不會解密,它會重新哈希)。

含鹽無助於您蠻力。鹽是哈希表的重要組成部分,就像不倒翁是鎖的一部分一樣。如果您取出其中任何一個,則沒人可以使用它。

分隔存儲毫無意義。在大多數情況下,您必須重新組裝完成的哈希以對其進行驗證。這就是為什麼默認情況下所有組件都存儲在一個方便的字符串中的原因。

您用更少的語言比我更優雅地解釋了它。+1
是的,分隔是沒有意義的-您不能在沒有另一個的情況下使用一個,在沒有另一個的情況下永遠不能更新它們,並且它們具有相同的安全性要求,因此請將salt + password視為一個不可分割的單個存儲單元。
keithRozario
2018-04-06 22:00:48 UTC
view on stackexchange narkive permalink

是的,您可以將其存儲在單個字段中,並且許多數據庫/應用程序將salt + hash存儲在單個字段/文件中。

最著名的是Linux(不是Linux DB),使用以下格式將哈希存儲在/ etc / shadow文件中:

“ $ id $ salt $ hashed”,這是由crypt(C)產生的密碼哈希的可打印形式,其中“ $ id”是使用的算法。 (在GNU / Linux上,“ $ 1 $”代表MD5,“ $ 2a $”代表Blowfish,“ $ 2y $”代表Blowfish(8位字符的正確處理),“ $ 5 $”代表SHA-256,“ $ 6 $ “是SHA-512,[4]其他Unix可能具有不同的值,例如NetBSD。

(來源: https://en.wikipedia.org/wiki/Passwd

鹽並不意味著是秘密的(或至少不比散列更秘密),其主要目的是使暴力破解更加困難,因為攻擊者必須使用

但是您的問題更加細微-因為您不僅在詢問鹽,而且還詢問參數,例如哈希算法,迭代計數和鹽。

假設您有很多用戶,並且您使用SHA1作為哈希算法,那麼您的數據庫字段將是類似 SHA1:SALT:HASH 的東西。

如果您想將數據庫升級到BCRYPT,您將如何做?

Typi毫無疑問,您將部署一些代碼,以便在用戶登錄時驗證密碼,如果有效,則可以使用較新的算法重新哈希密碼。現在,該用戶的字段如下所示: BCRYPT:SALT:HASH

但是隨後某些用戶將位於SHA1上,而其他用戶將位於BCRYPT上,因為該用戶位於用戶級別,您需要使用參數來告訴您的代碼哪些用戶將屬於數據庫中的用戶。

簡而言之,將參數和哈希存儲在單個字段中是可以的,但是出於任何原因將它們拆分出來(效率,更簡單的代碼等)也可以。不能的是將其存儲在您的代碼中:)

TL:DR

特洛伊·亨特(Troy Hunt)最近發布了一個播客,建議與採用上述方式遷移到BCRYPT相比,簡單地獲取當前正在使用的所有SHA1哈希更為有效。

有效地 BCRYPT(SHA1(clear_password))

當用戶登錄時,

  BCRYPT(SHA1(clear_password))== <db_field>  

通過這種方式,平台上的每個人都可以一次升級,而您的數據庫沒有多個哈希密碼格式。非常乾淨而且非常好。

我認為這個想法很合理,但是即使每個人都一次遷移,它也不是瞬時的。除非您願意接受應用程序的某些停機時間(在重新哈希所有密碼的同時),否則仍有一些時間間隔,有些用戶使用BCRYPT,有些用戶使用SHA1,因此您的數據庫仍應存儲哈希算法的參數,您的代碼將基於該算法執行。

您知道[我們為什麼不應該自己動手?](https://security.stackexchange.com/q/18197/114527)嗎?是否已經“證明” BCRYPT(SHA1())沒有這個問題?(您沒有提供鏈接以便我可以輕鬆地進行調查,並且“完全有意義”與“確實如此”不同)。
好吧,我確實用“我認為”警告了這句話,這是很合理的:)。我的理解是,如果BCRYPT通過使暴力破解變得更加昂貴來工作,那麼無論輸入什麼,無論是SHA1還是明文,對於攻擊者而言,暴力破解仍然會更加昂貴。從遷移的角度來看,BCRYPT(SHA1())是有意義的。
我也在這裡找到了這個主題,它建議您在BCRYPT輸入密碼之前先對密碼進行SHA輸入的其他一些用例。線程的普遍共識是這樣做是可以的,但是如果您仍然不同意https://security.stackexchange.com/questions/61595/is-it-good-practice-to-sha512,也許我們應該再提出一個問題密碼先傳遞給他們進行加密
@keithRozario“從直觀上看,BCRYPT(SHA1(x))至少與BCRYPT(x)一樣安全”與“有……的安全證明”不同。密碼學的第一法則:除非您的名字是djb或類似名稱,否則請不要相信您的直覺。
@MartinBonner是正確的。* IS *直觀,但這也是錯誤的。假設SHA1已損壞。通過使所有輸入返回1來模擬此情況。在這種情況下,BYCRYPT(SHA1(x))顯然不如BCRYPT(x)安全,類似地考慮SHA1(BCRYPT(x))。取決於* SHA1的斷開方式*散列*可以*保持有效。但是,它永遠不會比僅使用兩者中的更高安全性更高。由於理想情況下它們會影響安全方程式中的相同參數。*如果*他們的交互鏈最少,可能會有用,但這是很多數學。因此*經過驗證* @ AndrewMorton
@AndrewMorton很好的討論,但我不相信。您通過使所有輸入都返回1來模擬損壞的SHA1的示例是不正確的,這就像是說如果用戶將其密碼選擇為1,則BCRYPT就會損壞。但是我正在啟動另一個線程,因為我認為這很有趣。
新線程在這裡:https://security.stackexchange.com/questions/183358/how-secure-is-bcryptsha1password
@keithRozario是布萊克寫的。
TTT
2018-04-07 00:27:25 UTC
view on stackexchange narkive permalink

從安全的角度來看,salt和哈希密碼存儲在單個字段還是單獨的字段中都沒有關係,儘管為簡單起見,我傾向於使用單個字段。分開它們的唯一原因是,如果您的應用程序代碼需要將鹽和密碼分別傳遞給驗證功能。如果您的應用程序代碼只需要一個組合的字符串,那麼您也可以用這種方式存儲它。

例如,較早的ASP.NET Membership將密碼哈希和salt分為兩個DB字段,並且較新的ASP.NET Identity在單個DB字段中具有哈希和鹽,因此又對較新的功能進行了修改,以將單個字符串作為輸入和輸出來處理。

Andrew Morton
2018-04-08 00:36:49 UTC
view on stackexchange narkive permalink

可以將其存儲在SQL數據庫的一個字段中(在這種情況下為varchar(99))

否。數據的大小可能會在某些時候改變。除非規範指出大小始終為99,並且該 ever 不得有任何變化,否則應使用varchar(MAX)字段。讓數據庫滿足存儲需求。

ArtisticPhoenix
2018-04-08 05:42:05 UTC
view on stackexchange narkive permalink

要知道它的安全性,我們必須了解鹽的用途。

鹽有一些用途(我能想到)

  1. 防止字典攻擊
  2. ol>

    字典攻擊是將邏輯詞用作密碼的一部分。原型是單詞 password 甚至是 P @ ssW0rd 。人是不完美的,與單詞和數字的隨機字符串相比,他們更容易記住單詞。字典攻擊依靠此來縮短暴力攻擊的路徑。顯然,如果我們向其中添加隨機的內容,那麼這將使這種類型的攻擊變得不可能或毫無意義,並將它們重新強加於人。

    如果攻擊者知道使用的鹽,他們將必須編輯每個密碼的所有字典文件,並且假定他們知道如何將鹽添加到原始密碼中。詞典列表的一大好處是說您需要1000個密碼和100萬個詞典單詞。然後,您循環瀏覽它們,以嘗試使用較弱的密碼進行一些匹配。因此,必須將一百萬個單詞編輯1000次實際上是不實際的。

    1. 防止哈希或Rainbow表攻擊。
    2. ol>

      A Rainbow表是預先計算哈希並將其存儲在某種數據庫中的地方。例如,您可以使用MD5,然後開始對隨機內容進行哈希處理並保存。然後,當您想破解密碼時,只需在Rainbow表中查找即可。

      如果攻擊者吃了鹽,他們必須為使用的每種鹽重新編譯彩虹表。這種攻擊的好處是可以提前編譯數據並一再重複使用。這使他們重新使用蠻橫的密碼。通過對所有密碼加鹽和加鹽,這基本上意味著您必須重新計算每種鹽的彩虹表。這就是為什麼在每個密碼前使用不同的鹽很重要。

      摘要

      這些原因中沒有一個真正依賴於鹽的秘密。顯然,如果攻擊者知道了鹽分,它們的強度都會降低。但是他們獲得的其他信息將始終為他們提供幫助。因此,我不會說要去做廣告,但是有一種說法是基於模糊性的……。

      免責聲明我絕對不是密碼專家,但是這些是我想到鹽時想到的幾個最重要的原因。

      希望有幫助。

您對原因三有誤。Salt是公開的,因此攻擊者仍然可以使用所有1個字符的密碼,所有2個字符的密碼等。Salt所做的* only *事情是指攻擊者一次只能操作一個用戶,
@MartinBonner-很好,我將其刪除了...
maggick
2018-04-06 19:36:48 UTC
view on stackexchange narkive permalink

鹽僅用於阻止彩虹表攻擊,因此鹽可能會使用哈希密碼存儲,就像在最新的GNU / Linux系統上將鹽存儲在/ etc / shadow中一樣。

我不確定您的答案如何適用於不同的數據庫列?


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...