日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
如何安全的存儲(chǔ)用戶的密碼

大多數(shù)的web開(kāi)發(fā)者都會(huì)遇到設(shè)計(jì)用戶賬號(hào)系統(tǒng)的需求。賬號(hào)系統(tǒng)最重要的一個(gè)方面就是如何保護(hù)用戶的密碼。一些大公司的用戶數(shù)據(jù)庫(kù)泄露事件也時(shí)有發(fā)生,所以我們必須采取一些措施來(lái)保護(hù)用戶的密碼,即使網(wǎng)站被攻破的情況下也不會(huì)造成較大的危害。如果你還在存儲(chǔ)用戶密碼的MD5,那可真的有點(diǎn)弱了。趕緊來(lái)看看這篇文章吧。

保護(hù)密碼最好的的方式就是使用帶鹽的密碼hash(salted password hashing).對(duì)密碼進(jìn)行hash操作是一件很簡(jiǎn)單的事情,但是很多人都犯了錯(cuò)。接下來(lái)我希望可以詳細(xì)的闡述如何恰當(dāng)?shù)膶?duì)密碼進(jìn)行hash,以及為什么要這樣做。

重要提醒

如果你打算自己寫(xiě)一段代碼來(lái)進(jìn)行密碼hash,那么趕緊停下吧。這樣太容易犯錯(cuò)了。這個(gè)提醒適用于每一個(gè)人,不要自己寫(xiě)密碼的hash算法 !關(guān)于保存密碼的問(wèn)題已經(jīng)有了成熟的方案,那就是使用phpass或者本文提供的源碼。

什么是hash

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
hash("waltz") = c0e81794384491161f1777c232bc6bd9ec38f616560b120fda8e90f383853542

Hash算法是一種單向的函數(shù)。它可以把任意數(shù)量的數(shù)據(jù)轉(zhuǎn)換成固定長(zhǎng)度的“指紋”,這個(gè)過(guò)程是不可逆的。而且只要輸入發(fā)生改變,哪怕只有一個(gè)bit,輸出的hash值也會(huì)有很大不同。這種特性恰好合適用來(lái)用來(lái)保存密碼。因?yàn)槲覀兿M褂靡环N不可逆的算法來(lái)加密保存的密碼,同時(shí)又需要在用戶登陸的時(shí)候驗(yàn)證密碼是否正確。

在一個(gè)使用hash的賬號(hào)系統(tǒng)中,用戶注冊(cè)和認(rèn)證的大致流程如下:

1, 用戶創(chuàng)建自己的賬號(hào)
2, 用戶密碼經(jīng)過(guò)hash操作之后存儲(chǔ)在數(shù)據(jù)庫(kù)中。沒(méi)有任何明文的密碼存儲(chǔ)在服務(wù)器的硬盤(pán)上。
3, 用戶登陸的時(shí)候,將用戶輸入的密碼進(jìn)行hash操作后與數(shù)據(jù)庫(kù)里保存的密碼hash值進(jìn)行對(duì)比。
4, 如果hash值完全一樣,則認(rèn)為用戶輸入的密碼是正確的。否則就認(rèn)為用戶輸入了無(wú)效的密碼。
5, 每次用戶嘗試登陸的時(shí)候就重復(fù)步驟3和步驟4。

在步驟4的時(shí)候不要告訴用戶是賬號(hào)還是密碼錯(cuò)了。只需要顯示一個(gè)通用的提示,比如賬號(hào)或密碼不正確就可以了。這樣可以防止攻擊者枚舉有效的用戶名。

還需要注意的是用來(lái)保護(hù)密碼的hash函數(shù)跟數(shù)據(jù)結(jié)構(gòu)課上見(jiàn)過(guò)的hash函數(shù)不完全一樣。比如實(shí)現(xiàn)hash表的hash函數(shù)設(shè)計(jì)的目的是快速,但是不夠安全。只有加密hash函數(shù)(cryptographic hash functions)可以用來(lái)進(jìn)行密碼的hash。這樣的函數(shù)有SHA256, SHA512, RipeMD, WHIRLPOOL等。

一個(gè)常見(jiàn)的觀念就是密碼經(jīng)過(guò)hash之后存儲(chǔ)就安全了。這顯然是不正確的。有很多方式可以快速的從hash恢復(fù)明文的密碼。還記得那些md5破解網(wǎng)站吧,只需要提交一個(gè)hash,不到一秒鐘就能知道結(jié)果。顯然,單純的對(duì)密碼進(jìn)行hash還是遠(yuǎn)遠(yuǎn)達(dá)不到我們的安全需求。下一部分先討論一下破解密碼hash,獲取明文常見(jiàn)的手段。#p#

如何破解hash

字典和暴力破解攻擊(Dictionary and Brute Force Attacks)

最常見(jiàn)的破解hash手段就是猜測(cè)密碼。然后對(duì)每一個(gè)可能的密碼進(jìn)行hash,對(duì)比需要破解的hash和猜測(cè)的密碼hash值,如果兩個(gè)值一樣,那么之前猜測(cè)的密碼就是正確的密碼明文。猜測(cè)密碼攻擊常用的方式就是字典攻擊和暴力攻擊。

Dictionary Attack
Trying apple : failed
Trying blueberry : failed
Trying justinbeiber : failed
...
Trying letmein : failed
Trying s3cr3t : success!

字典攻擊是將常用的密碼,單詞,短語(yǔ)和其他可能用來(lái)做密碼的字符串放到一個(gè)文件中,然后對(duì)文件中的每一個(gè)詞進(jìn)行hash,將這些hash與需要破解的密碼hash比較。這種方式的成功率取決于密碼字典的大小以及字典的是否合適。

Brute Force Attack
Trying aaaa : failed
Trying aaab : failed
Trying aaac : failed
...
Trying acdb : failed
Trying acdc : success!

暴力攻擊就是對(duì)于給定的密碼長(zhǎng)度,嘗試每一種可能的字符組合。這種方式需要花費(fèi)大量的計(jì)算機(jī)時(shí)間。但是理論上只要時(shí)間足夠,最后密碼一定能夠破解出來(lái)。只是如果密碼太長(zhǎng),破解花費(fèi)的時(shí)間就會(huì)大到無(wú)法承受。

目前沒(méi)有方式可以阻止字典攻擊和暴力攻擊。只能想辦法讓它們變的低效。如果你的密碼hash系統(tǒng)設(shè)計(jì)的是安全的,那么破解hash唯一的方式就是進(jìn)行字典或者暴力攻擊了。

查表破解(Lookup Tables)

對(duì)于特定的hash類(lèi)型,如果需要破解大量hash的話,查表是一種非常有效而且快速的方式。它的理念就是預(yù)先計(jì)算(pre-compute)出密碼字典中每一個(gè)密碼的hash。然后把hash和對(duì)應(yīng)的密碼保存在一個(gè)表里。一個(gè)設(shè)計(jì)良好的查詢表結(jié)構(gòu),即使存儲(chǔ)了數(shù)十億個(gè)hash,每秒鐘仍然可以查詢成百上千個(gè)hash。

如果你想感受下查表破解hash的話可以嘗試一下在CraskStation上破解下下面的sha256 hash。

c11083b4b0a7743af748c85d343dfee9fbb8b2576c05f3a7f0d632b0926aadfc
08eac03b80adc33dc7d8fbe44b7c7b05d3a2c511166bdb43fcb710b03ba919e7
e4ba5cbd251c98e6cd1c23f126a3b81d8d8328abc95387229850952b3ef9f904
5206b8b8a996cf5320cb12ca91c7b790fba9f030408efe83ebb83548dc3007bd

反向查表破解(Reverse Lookup Tables)

Searching for hash(apple) in users' hash list... : Matches [alice3, 0bob0, charles8]
Searching for hash(blueberry) in users' hash list... : Matches [usr10101, timmy, john91]
Searching for hash(letmein) in users' hash list... : Matches [wilson10, dragonslayerX, joe1984]
Searching for hash(s3cr3t) in users' hash list... : Matches [bruce19, knuth1337, john87]
Searching for hash(z@29hjja) in users' hash list... : No users used this password

這種方式可以讓攻擊者不預(yù)先計(jì)算一個(gè)查詢表的情況下同時(shí)對(duì)大量hash進(jìn)行字典和暴力破解攻擊。

首先,攻擊者會(huì)根據(jù)獲取到的數(shù)據(jù)庫(kù)數(shù)據(jù)制作一個(gè)用戶名和對(duì)應(yīng)的hash表。然后將常見(jiàn)的字典密碼進(jìn)行hash之后,跟這個(gè)表的hash進(jìn)行對(duì)比,就可以知道用哪些用戶使用了這個(gè)密碼。這種攻擊方式很有效果,因?yàn)橥ǔG闆r下很多用戶都會(huì)有使用相同的密碼。

彩虹表 (Rainbow Tables)

彩虹表是一種使用空間換取時(shí)間的技術(shù)。跟查表破解很相似。只是它犧牲了一些破解時(shí)間來(lái)達(dá)到更小的存儲(chǔ)空間的目的。因?yàn)椴屎绫硎褂玫拇鎯?chǔ)空間更小,所以單位空間就可以存儲(chǔ)更多的hash。彩虹表已經(jīng)能夠破解8位長(zhǎng)度的任意md5hash。彩虹表具體的原理可以參考http://www.project-rainbowcrack.com/

下一章節(jié)我們會(huì)討論一種叫做“鹽”(salting)的技術(shù)。通過(guò)這種技術(shù)可以讓查表和彩虹表的方式無(wú)法破解hash。#p#

加鹽(Adding Salt)

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hello" + "QxLUF1bgIAdeQX") = 9e209040c863f84a31e719795b2577523954739fe5ed3b58a75cff2127075ed1
hash("hello" + "bv5PehSMfV11Cd") = d1d3ec2e6f20fd420d50e2642992841d8338a314b8ea157c9e18477aaef226ab
hash("hello" + "YYLmfY6IehjZMQ") = a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2c4a8544305df1b60f007

查表和彩虹表的方式之所以有效是因?yàn)槊恳粋€(gè)密碼的都是通過(guò)同樣的方式來(lái)進(jìn)行hash的。如果兩個(gè)用戶使用了同樣的密碼,那么一定他們的密碼hash也一定相同。我們可以通過(guò)讓每一個(gè)hash隨機(jī)化,同一個(gè)密碼hash兩次,得到的不同的hash來(lái)避免這種攻擊。

具體的操作就是給密碼加一個(gè)隨即的前綴或者后綴,然后再進(jìn)行hash。這個(gè)隨即的后綴或者前綴成為“鹽”。正如上面給出的例子一樣,通過(guò)加鹽,相同的密碼每次hash都是完全不一樣的字符串了。檢查用戶輸入的密碼是否正確的時(shí)候,我們也還需要這個(gè)鹽,所以鹽一般都是跟hash一起保存在數(shù)據(jù)庫(kù)里,或者作為hash字符串的一部分。

鹽不需要保密,只要鹽是隨機(jī)的話,查表,彩虹表都會(huì)失效。因?yàn)楣粽邿o(wú)法事先知道鹽是什么,也就沒(méi)有辦法預(yù)先計(jì)算出查詢表和彩虹表。如果每個(gè)用戶都是使用了不同的鹽,那么反向查表攻擊也沒(méi)法成功。

下一節(jié),我們會(huì)介紹一些鹽的常見(jiàn)的錯(cuò)誤實(shí)現(xiàn)。

錯(cuò)誤的方式:短的鹽和鹽的復(fù)用

最常見(jiàn)的錯(cuò)誤實(shí)現(xiàn)就是一個(gè)鹽在多個(gè)hash中使用或者使用的鹽很短。

鹽的復(fù)用(Salt Reuse)

不管是將鹽硬編碼在程序里還是隨機(jī)一次生成的,在每一個(gè)密碼hash里使用相同的鹽會(huì)使這種防御方法失效。因?yàn)橄嗤拿艽ahash兩次得到的結(jié)果還是相同的。攻擊者就可以使用反向查表的方式進(jìn)行字典和暴力攻擊。只要在對(duì)字典中每一個(gè)密碼進(jìn)行hash之前加上這個(gè)固定的鹽就可以了。如果是流行的程序的使用了硬編碼的鹽,那么也可能出現(xiàn)針對(duì)這種程序的這個(gè)鹽的查詢表和彩虹表,從而實(shí)現(xiàn)快速破解hash。

用戶每次創(chuàng)建或者修改密碼一定要使用一個(gè)新的隨機(jī)的鹽

短的鹽

如果鹽的位數(shù)太短的話,攻擊者也可以預(yù)先制作針對(duì)所有可能的鹽的查詢表。比如,3位ASCII字符的鹽,一共有95x95x95 = 857,375種可能性??雌饋?lái)好像很多。假如每一個(gè)鹽制作一個(gè)1MB的包含常見(jiàn)密碼的查詢表,857,375個(gè)鹽才是837GB?,F(xiàn)在買(mǎi)個(gè)1TB的硬盤(pán)都只要幾百塊而已。

基于同樣的理由,千萬(wàn)不要用用戶名做為鹽。雖然對(duì)于每一個(gè)用戶來(lái)說(shuō)用戶名可能是不同的,但是用戶名是可預(yù)測(cè)的,并不是完全隨機(jī)的。攻擊者完全可以用常見(jiàn)的用戶名作為鹽來(lái)制作查詢表和彩虹表破解hash。

根據(jù)一些經(jīng)驗(yàn)得出來(lái)的規(guī)則就是鹽的大小要跟hash函數(shù)的輸出一致。比如,SHA256的輸出是256bits(32bytes),鹽的長(zhǎng)度也應(yīng)該是32個(gè)字節(jié)的隨機(jī)數(shù)據(jù)。#p#

錯(cuò)誤的方式:雙重hash和古怪的hash函數(shù)

這一節(jié)討論另外一個(gè)常見(jiàn)的hash密碼的誤解:古怪的hash算法組合。人們可能解決的將不同的hash函數(shù)組合在一起用可以讓數(shù)據(jù)更安全。但實(shí)際上,這種方式帶來(lái)的效果很微小。反而可能帶來(lái)一些互通性的問(wèn)題,甚至有時(shí)候會(huì)讓hash更加的不安全。本文一開(kāi)始就提到過(guò),永遠(yuǎn)不要嘗試自己寫(xiě)hash算法,要使用專家們?cè)O(shè)計(jì)的標(biāo)準(zhǔn)算法。有些人會(huì)覺(jué)得通過(guò)使用多個(gè)hash函數(shù)可以降低計(jì)算hash的速度,從而增加破解的難度。通過(guò)減慢hash計(jì)算速度來(lái)防御攻擊有更好的方法,這個(gè)下文會(huì)詳細(xì)介紹。

下面是一些網(wǎng)上找到的古怪的hash函數(shù)組合的樣例。

md5(sha1(password))
md5(md5(salt) + md5(password))
sha1(sha1(password))
sha1(str_rot13(password + salt))
md5(sha1(md5(md5(password) + sha1(password)) + md5(password)))

不要使用他們!

注意:這部分的內(nèi)容其實(shí)是存在爭(zhēng)議的!我收到過(guò)大量郵件說(shuō)組合hash函數(shù)是有意義的。因?yàn)槿绻粽卟恢牢覀冇昧四膫€(gè)函數(shù),就不可能事先計(jì)算出彩虹表,并且組合hash函數(shù)需要更多的計(jì)算時(shí)間。

攻擊者如果不知道hash算法的話自然是無(wú)法破解hash的。但是考慮到Kerckhoffs’s principle,攻擊者通常都是能夠接觸到源碼的(尤其是免費(fèi)軟件和開(kāi)源軟件)。通過(guò)一些目標(biāo)系統(tǒng)的密碼–hash對(duì)應(yīng)關(guān)系來(lái)逆向出算法也不是非常困難。

如果你想使用一個(gè)標(biāo)準(zhǔn)的”古怪”的hash函數(shù),比如HMAC,是可以的。但是如果你的目的是想減慢hash的計(jì)算速度,那么可以讀一下后面討論的慢速hash函數(shù)部分?;谏厦嬗懻摰囊蛩兀詈玫淖龇ㄊ鞘褂脴?biāo)準(zhǔn)的經(jīng)過(guò)嚴(yán)格測(cè)試的hash算法。

hash碰撞(Hash Collisions)

因?yàn)閔ash函數(shù)是將任意數(shù)量的數(shù)據(jù)映射成一個(gè)固定長(zhǎng)度的字符串,所以一定存在不同的輸入經(jīng)過(guò)hash之后變成相同的字符串的情況。加密hash函數(shù)(Cryptographic hash function)在設(shè)計(jì)的時(shí)候希望使這種碰撞攻擊實(shí)現(xiàn)起來(lái)成本難以置信的高。但時(shí)不時(shí)的就有密碼學(xué)家發(fā)現(xiàn)快速實(shí)現(xiàn)hash碰撞的方法。最近的一個(gè)例子就是MD5,它的碰撞攻擊已經(jīng)實(shí)現(xiàn)了。

碰撞攻擊是找到另外一個(gè)跟原密碼不一樣,但是具有相同hash的字符串。但是,即使在相對(duì)弱的hash算法,比如MD5,要實(shí)現(xiàn)碰撞攻擊也需要大量的算力(computing power),所以在實(shí)際使用中偶然出現(xiàn)hash碰撞的情況幾乎不太可能。一個(gè)使用加鹽MD5的密碼hash在實(shí)際使用中跟使用其他算法比如SHA256一樣安全。不過(guò)如果可以的話,使用更安全的hash函數(shù),比如SHA256, SHA512, RipeMD, WHIRLPOOL等是更好的選擇。#p#

正確的方式:如何恰當(dāng)?shù)倪M(jìn)行hash

這部分會(huì)詳細(xì)討論如何恰當(dāng)?shù)倪M(jìn)行密碼hash。第一個(gè)章節(jié)是最基礎(chǔ)的,這章節(jié)的內(nèi)容是必須的。后面一個(gè)章節(jié)是闡述如何繼續(xù)增強(qiáng)安全性,讓hash破解變得異常困難。

基礎(chǔ):使用加鹽hash

我們已經(jīng)知道惡意黑客可以通過(guò)查表和彩虹表的方式快速的獲得hash對(duì)應(yīng)的明文密碼,我們也知道了通過(guò)使用隨機(jī)的鹽可以解決這個(gè)問(wèn)題。但是我們?cè)趺瓷甥},怎么在hash的過(guò)程中使用鹽呢?

鹽要使用密碼學(xué)上可靠安全的偽隨機(jī)數(shù)生成器(Cryptographically Secure Pseudo-Random Number Generator (CSPRNG))來(lái)產(chǎn)生。CSPRNG跟普通的偽隨機(jī)數(shù)生成器比如C語(yǔ)言中的rand(),有很大不同。正如它的名字說(shuō)明的那樣,CSPRNG提供一個(gè)高標(biāo)準(zhǔn)的隨機(jī)數(shù),是完全無(wú)法預(yù)測(cè)的。我們不希望我們的鹽能夠被預(yù)測(cè)到,所以一定要使用CSPRNG。下表提供了一些常用語(yǔ)言中的CSPRNG。

每一個(gè)用戶,每一個(gè)密碼都要使用不同的鹽。用戶每次創(chuàng)建賬戶或者修改密碼都要使用一個(gè)新的隨機(jī)鹽。永遠(yuǎn)不要重復(fù)使用鹽。鹽的長(zhǎng)度要足夠,一個(gè)經(jīng)驗(yàn)規(guī)則就是鹽的至少要跟hash函數(shù)輸出的長(zhǎng)度一致。鹽應(yīng)該跟hash一起存儲(chǔ)在用戶信息表里。

存儲(chǔ)一個(gè)密碼:

1, 使用CSPRNG生成一個(gè)長(zhǎng)的隨機(jī)鹽。

2, 將密碼和鹽拼接在一起,使用標(biāo)準(zhǔn)的加密hash函數(shù)比如SHA256進(jìn)行hash

3, 將鹽和hash記錄在用戶數(shù)據(jù)庫(kù)中

驗(yàn)證一個(gè)密碼:

1, 從數(shù)據(jù)庫(kù)中取出用戶的鹽和hash

2, 將用戶輸入的密碼和鹽按相同方式拼接在一起,使用相同的hash函數(shù)進(jìn)行hash

3, 比較計(jì)算出的hash跟存儲(chǔ)的hash是否相同。如果相同則密碼正確。反之則密碼錯(cuò)誤。

在本文的最后,給出了php,C#,Java,Ruby的加鹽密碼hash的實(shí)現(xiàn)代碼。

在web應(yīng)用中,要在服務(wù)端進(jìn)行hash:

如果你在寫(xiě)一個(gè)web應(yīng)用,可能會(huì)有在客戶端還是服務(wù)端進(jìn)行hash的疑惑。是將密碼在瀏覽器里使用javascript進(jìn)行hash,還是將明文傳給服務(wù)端,在服務(wù)端進(jìn)行hash呢?

即使在客戶端用javascript進(jìn)行了hash,在服務(wù)端依然需要將得到的密碼hash再進(jìn)行hash。如果不這么做的話,認(rèn)證用戶的時(shí)候,服務(wù)端是獲取了瀏覽器傳過(guò)來(lái)的hash跟數(shù)據(jù)庫(kù)里的hash比較。這樣子看起來(lái)是更安全了,因?yàn)闆](méi)有明文密碼傳送到服務(wù)端。但是事實(shí)上卻不是這樣。

問(wèn)題在于這樣的話,如果惡意的黑客獲取了用戶的hash,就可以直接用來(lái)登陸用戶的賬號(hào)了。甚至都不需要知道用戶的明文密碼!也就不需要破解hash了。

這并不是說(shuō)你完全不能在瀏覽器端進(jìn)行hash。只是如果你要這樣做的話,一定要在服務(wù)端再hash一次。在瀏覽器端進(jìn)行hash是一個(gè)不錯(cuò)的想法,但是在實(shí)現(xiàn)的時(shí)候一定要考慮到以下幾點(diǎn):

1, 客戶端密碼hash并不是HTTPS(SSL/TLS)的替代品。如果瀏覽器和服務(wù)器之間的連接是不安全的,中間人(man-in-the-middle)可能通過(guò)修改網(wǎng)頁(yè)的加載的javascript移除掉hash函數(shù)來(lái)得到用戶的明文密碼。

2, 有些瀏覽器可能不支持javascript,有些用戶也會(huì)禁用javascript。為了更好的兼容性,需要檢測(cè)用戶的瀏覽器是否支持javascript,如果不支持的話就需要在服務(wù)端模擬客戶端hash的邏輯。

3, 客戶端的hash也需要加鹽。一個(gè)很容想到的方式就是使用客戶端腳本請(qǐng)求服務(wù)器或得用戶的鹽。記住,不要使用這種方式。因?yàn)檫@樣惡意攻擊者就可以通過(guò)這個(gè)邏輯來(lái)判斷一個(gè)用戶名是否有效。因?yàn)槲覀円呀?jīng)在服務(wù)端進(jìn)行了恰當(dāng)?shù)募欲}的hash。所以這里使用用戶名跟特定的字符串(比如域名)拼接作為客戶端的鹽是可以的。

使用慢速hash函數(shù)讓破解更加困難:

加鹽可以讓攻擊者無(wú)法使用查表和彩虹表的方式對(duì)大量hash進(jìn)行破解。但是依然無(wú)法避免對(duì)單個(gè)hash的字典和暴力攻擊。高端的顯卡(GPUs)和一些定制的硬件每秒可以計(jì)算數(shù)十億的hash,所以針對(duì)單個(gè)hash的攻擊依然有效。為了避免字典和暴力攻擊,我們可以采用一種稱為key擴(kuò)展(key stretching)的技術(shù)。

思路就是讓hash的過(guò)程便得非常緩慢,即使使用高速GPU和特定的硬件,字典和暴力破解的速度也慢到?jīng)]有實(shí)用價(jià)值。通過(guò)減慢hash的過(guò)程來(lái)防御攻擊,但是hash速度依然可以保證用戶使用的時(shí)候沒(méi)有明顯的延遲。

key擴(kuò)展的實(shí)現(xiàn)是使用一種大量消耗cpu資源的hash函數(shù)。不要去使用自己創(chuàng)造的迭代hash函數(shù),那是不夠的。要使用標(biāo)準(zhǔn)算法的hash函數(shù),比如PBKDF2或者bcrypt。PHP實(shí)現(xiàn)可以在這里找到。

這些算法采用了一個(gè)安全變量或者迭代次數(shù)作為參數(shù)。這個(gè)值決定了hash的過(guò)程具體有多慢。對(duì)于桌面軟件和手機(jī)APP,確定這個(gè)參數(shù)的最好方式是在設(shè)備上運(yùn)行一個(gè)標(biāo)準(zhǔn)測(cè)試程序得到hash時(shí)間大概在半秒左右的值。這樣就可以避免暴力攻擊,也不會(huì)影響用戶體驗(yàn)。

如果是在web應(yīng)用中使用key擴(kuò)展hash函數(shù),需要考慮可能有大量的計(jì)算資源用來(lái)處理用戶認(rèn)證請(qǐng)求。攻擊者可能通過(guò)這種方式來(lái)進(jìn)行拒絕服務(wù)攻擊。不過(guò)我依然推薦使用key擴(kuò)展hash函數(shù),只是迭代次數(shù)設(shè)置的小一點(diǎn)。這個(gè)次數(shù)需要根據(jù)自己服務(wù)器的計(jì)算能力和預(yù)計(jì)每秒需要處理的認(rèn)證請(qǐng)求次數(shù)來(lái)設(shè)置。對(duì)于拒絕服務(wù)攻擊可以通過(guò)讓用戶登陸的時(shí)候輸入驗(yàn)證碼的方式來(lái)防御。系統(tǒng)設(shè)計(jì)的時(shí)候一定要考慮到這個(gè)迭代次數(shù)將來(lái)可以方便的增加或降低。

如果你擔(dān)心計(jì)算機(jī)的能力不夠強(qiáng),而又希望在自己的web應(yīng)用中使用key擴(kuò)展hash函數(shù),可以考慮在用戶的瀏覽器運(yùn)行hash函數(shù)。Stanford JavaScript Crypto Library包含了PBKDF2算法。在瀏覽器中進(jìn)行hash需要考慮上面提到的幾個(gè)方面。#p#

理論上不可能破解的hash:使用加密的key和密碼hash硬件

只要攻擊者能夠驗(yàn)證一個(gè)猜測(cè)的密碼是正確還是錯(cuò)誤,他們都可以使用字典或者暴力攻擊破解hash。更深度的防御方法是加入一個(gè)保密的key(secret key)進(jìn)行hash,這樣只有知道這個(gè)key的人才能驗(yàn)證密碼是否正確。這個(gè)可以通過(guò)兩種方式來(lái)實(shí)現(xiàn)。一種是hash通過(guò)加密算法加密比如AES,或者使用基于key的hash函數(shù)(HMAC)。

這個(gè)實(shí)現(xiàn)起來(lái)并不容易。key一定要做到保密,即使系統(tǒng)被攻破也不能泄露才行。但是如果攻擊者獲取了系統(tǒng)權(quán)限,無(wú)論key保存在哪里,都可能被獲取到。所以這個(gè)key一定要保存在一個(gè)外部系統(tǒng)中,比如專門(mén)用來(lái)進(jìn)行密碼驗(yàn)證的物理隔離的服務(wù)器?;蚴鞘褂冒惭b在服務(wù)器上特殊硬件,比如YubiHSM。

強(qiáng)烈建議所有大型的服務(wù)(超過(guò)10萬(wàn)用戶)的公司使用這種方式。對(duì)于超過(guò)100萬(wàn)用戶的服務(wù)商一定得采用這種方式保護(hù)用戶信息。

如果條件不允許使用專用驗(yàn)證的服務(wù)器和特殊的硬件,依然從這種方式中受益。大部分?jǐn)?shù)據(jù)庫(kù)泄露都是利用了SQL注入技術(shù)。sql注入大部分情況下,攻擊者都沒(méi)法讀取服務(wù)器上的任意文件(關(guān)閉數(shù)據(jù)庫(kù)服務(wù)器的文件權(quán)限)。如果你生成了一個(gè)隨機(jī)的key,把它保存在了一個(gè)文件里。并且密碼使用了加密key的加鹽hash,單單sql注入攻擊導(dǎo)致的hash泄露并不會(huì)影響用戶的密碼。雖然這種方式不如使用獨(dú)立的系統(tǒng)來(lái)保存key安全,因?yàn)槿绻到y(tǒng)存在文件包含漏洞的話,攻擊者就可能讀取這個(gè)秘密文件了。不過(guò),使用了加密key總歸好過(guò)沒(méi)有使用吧。

需要注意使用key的hash并不是不需要加鹽,聰明的攻擊者總是會(huì)找到辦法獲取到key的。所以讓hash在鹽和key擴(kuò)展的保護(hù)下非常重要。

其他的安全措施

密碼hash僅僅是在發(fā)生安全事故的時(shí)候保護(hù)密碼。它并不能讓?xiě)?yīng)用程序更加安全。對(duì)于保護(hù)用戶密碼hash更多的是需要保護(hù)密碼hash不被偷走。

即使經(jīng)驗(yàn)豐富的程序也需要經(jīng)過(guò)安全培訓(xùn)才能寫(xiě)出安全的應(yīng)用。一個(gè)不錯(cuò)的學(xué)習(xí)web應(yīng)用漏洞的資源是OWASP。除非你理解了OWASP Top Ten Vulnerability List,否則不要去寫(xiě)關(guān)系到敏感數(shù)據(jù)的程序。公司有責(zé)任確保所有的開(kāi)發(fā)者都經(jīng)過(guò)了足夠的安全開(kāi)發(fā)的培訓(xùn)。

通過(guò)第三方的滲透測(cè)試也是不錯(cuò)的方式。即使最好的程序員也會(huì)犯錯(cuò),所以讓安全專家來(lái)審計(jì)代碼總是有意義的。尋找一個(gè)可信賴的第三方或者自己招聘一個(gè)安全人員來(lái)機(jī)型定期的代碼審計(jì)。安全評(píng)審要在應(yīng)用生命周期的早期就開(kāi)始并且貫穿整個(gè)開(kāi)發(fā)過(guò)程。

對(duì)網(wǎng)站進(jìn)行入侵監(jiān)控也十分重要。我建議至少招聘一名全職的安全人員進(jìn)行入侵檢測(cè)和安全事件響應(yīng)。如果入侵沒(méi)有檢測(cè)到,攻擊者可能讓在你的網(wǎng)站上掛馬影響你的用戶。所以迅速的入侵檢測(cè)和響應(yīng)也很重要。#p#

經(jīng)常提問(wèn)的問(wèn)題

我應(yīng)該使用什么hash算法

可以使用

1, 本文最后介紹的代碼

2, OpenWall的Portable PHP password hashing framework

3, 經(jīng)過(guò)充分測(cè)試的加密hash函數(shù),比如SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3等

4, 設(shè)計(jì)良好的key擴(kuò)展hash算法,比如PBKDF2,bcrypt,scrypt

5, crypt#Library_Function_crypt.283.29)的安全版本。($2y$, $5$, $6$)

不要使用

1, 過(guò)時(shí)的hash函數(shù),比如MD5,SHA1

2, crypt的不安全版本。($1$, $2$, $2x$, $3$)

3, 任何自己設(shè)計(jì)的算法。

盡管MD5和SHA1并沒(méi)有密碼學(xué)方面的攻擊導(dǎo)致它們生成的hash很容易被破解,但是它們年代很古老了,通常都認(rèn)為(可能有一些不恰當(dāng))它們不合適用來(lái)進(jìn)行密碼的存儲(chǔ)。所以我不推薦使用它們。對(duì)于這個(gè)規(guī)則有個(gè)例外就是PBKDF2,它使用SHA1作為它的基礎(chǔ)算法。

當(dāng)用戶忘記密碼的時(shí)候我應(yīng)該怎樣讓他們重置

在我個(gè)人看來(lái)現(xiàn)在外面廣泛使用的密碼重置機(jī)制都是不安全的,如果你有很高的安全需求,比如重要的加密服務(wù),那么不要讓用戶重置他們的密碼。

大多數(shù)網(wǎng)站使用綁定的email來(lái)進(jìn)行密碼找回。通過(guò)生成一個(gè)隨機(jī)的只使用一次的token,這個(gè)token必須跟賬戶綁定,然后把密碼重置的鏈接發(fā)送到用戶郵箱中。當(dāng)用戶點(diǎn)擊密碼重置鏈接的時(shí)候,提示他們輸入新的密碼。需要注意token一定要綁定到用戶以免攻擊者使用發(fā)送給自己的token來(lái)修改別人的密碼。

token一定要設(shè)置成15分鐘后或者使用一次后作廢。當(dāng)用戶登陸或者請(qǐng)求了一個(gè)新的token的時(shí)候,之前發(fā)送的token都作廢也是不錯(cuò)的主意。如果token不失效的話,那么就可以用來(lái)永久控制這個(gè)賬戶了。Email(SMTP)是明文傳輸?shù)膮f(xié)議,而互聯(lián)網(wǎng)上可能有很多惡意的路由器記錄email流量。并且用戶的email賬號(hào)也可能被盜。使token盡可能快的失效可以降低上面提到的這些風(fēng)險(xiǎn)。

用戶可能?chē)L試去修改token,所以不要在token里存儲(chǔ)任何賬戶信息。token應(yīng)該是一個(gè)不能被預(yù)測(cè)的隨機(jī)的二進(jìn)制塊(binary blob),僅僅用來(lái)進(jìn)行識(shí)別的一條記錄。

永遠(yuǎn)不要通過(guò)email發(fā)送用戶的新密碼。記得用戶重置密碼的時(shí)候要重新生成鹽,不要使用之前舊密碼使用的鹽。

如果我的用戶數(shù)據(jù)庫(kù)泄露了,我應(yīng)該怎么辦

第一要做的就是弄明白信息是怎么泄露的,然后把漏洞修補(bǔ)好。

人們可能會(huì)想辦法掩蓋這次安全事件,希望沒(méi)有人知道。但是,嘗試掩蓋安全事件會(huì)讓你的處境變得更糟。因?yàn)槟悴桓嬷愕挠脩羲男畔⒑兔艽a可能泄露了會(huì)給用戶帶來(lái)更大的風(fēng)險(xiǎn)。一定要第一時(shí)間通知用戶發(fā)生了安全事件,即使你還沒(méi)有完全搞明白黑客到底滲透到了什么程度。在首頁(yè)上放一個(gè)提醒,然后鏈接到詳細(xì)說(shuō)明的頁(yè)面。如果可能的話給每一個(gè)用戶發(fā)送email提醒。

向你的用戶詳細(xì)的說(shuō)明他的密碼是如何被保護(hù)的,希望是加鹽的hash,即使密碼進(jìn)行了加鹽hash保護(hù),攻擊者依然會(huì)進(jìn)行字典和暴力攻擊嘗試破解hash。攻擊者會(huì)使用發(fā)現(xiàn)的密碼嘗試登陸其他網(wǎng)站,因?yàn)橛脩艨赡茉诓煌木W(wǎng)站都使用了相同的密碼(所謂的撞庫(kù)攻擊)。告知你的用戶存在的這些風(fēng)險(xiǎn),建議他們修改使用了相同密碼的地方。在自己的網(wǎng)站上,下次用戶登陸的時(shí)候強(qiáng)制他們修改密碼。大部分用戶可能會(huì)嘗試使用相同的密碼,為了方便。要設(shè)計(jì)足夠的邏輯避免這樣的情況發(fā)生。

即使有了加鹽的hash,攻擊者也可能快速破解一些很弱的弱密碼。為了降低這種風(fēng)險(xiǎn),可以在使用正確密碼的前提下,加一個(gè)郵件認(rèn)證,直到用戶修改密碼。

還要告知你的用戶有哪些個(gè)人信息存儲(chǔ)在網(wǎng)站上。如果數(shù)據(jù)庫(kù)包含信用卡信息,你需要通知你的用戶注意自己近期的賬單,并且最好注銷(xiāo)掉這個(gè)信用卡。

應(yīng)該使用怎樣的密碼策略,需要強(qiáng)制使用強(qiáng)密碼么

如果你的服務(wù)不是有很?chē)?yán)格的安全需求,那么不要限制你的用戶。我建議在用戶輸入密碼的時(shí)候顯示它的強(qiáng)度等級(jí)。讓用戶自己決定使用什么強(qiáng)度的密碼。如果你的系統(tǒng)有很強(qiáng)的安全需求,那么強(qiáng)制用戶使用12位以上的密碼,至少包含2個(gè)數(shù)字,2個(gè)字母,2個(gè)字符。

每6個(gè)月最多強(qiáng)制用戶修改一次密碼。超過(guò)這個(gè)次數(shù),用戶就會(huì)感到疲勞。他們更傾向于選擇一個(gè)弱密碼。更應(yīng)該做的是教育你的用戶,當(dāng)他們感到自己的密碼可能泄露的時(shí)候主動(dòng)修改密碼。

如果攻擊者獲取了數(shù)據(jù)庫(kù)權(quán)限,他不能直接替換hash登陸任意賬戶么

當(dāng)然,不過(guò)如果他已經(jīng)或得了數(shù)據(jù)庫(kù)權(quán)限,很可能已經(jīng)可以獲得服務(wù)器上的所有信息了。所以沒(méi)有什么必要去修改hash登陸別人賬戶。進(jìn)行密碼hash的目的不是保護(hù)網(wǎng)站不被入侵,而是如果入侵發(fā)生了,可以更好的保護(hù)用戶的密碼。

在SQL注入攻擊中,保護(hù)hash不被替換的方式使用兩個(gè)用戶不同權(quán)限的用戶連接數(shù)據(jù)庫(kù)。一個(gè)具有寫(xiě)權(quán)限,另外一個(gè)只具有只讀的權(quán)限。

為什么需要一些特別的算法比如HMAC,而不是直接把密碼和加密key拼接在一起

(這部分講一些密碼學(xué)的原理,翻譯的不好請(qǐng)見(jiàn)諒)

hash函數(shù),比如MD5,SHA1,SHA2使用了Merkle–Damg?rd construction,這導(dǎo)致算法可能長(zhǎng)度擴(kuò)展攻擊(length extension attacks)。意思就是說(shuō)給定一個(gè)hash H(X),攻擊者可以在不知道X的情況下,可以找到一個(gè)H(pad(X)+Y)的值,Y是個(gè)其他的字符串。pad(X)是hash函數(shù)使用的填充函數(shù)(padding function)。

這就意味者,對(duì)于hash H(key + message),攻擊者可以計(jì)算 H(pad(key + message) + extension),并不需要知道加密key。如果這個(gè)hash是用在消息認(rèn)證過(guò)程中,使用key為了避免消息被修改。這樣的話這個(gè)系統(tǒng)就可能失效了,因?yàn)楣粽哒莆樟艘粋€(gè)有效的基于 message+extension的hash。

這種攻擊對(duì)于如何快速破解hash還不是很清楚。但是,基于一些風(fēng)險(xiǎn)的考慮,不建議使用單純的hash函數(shù)進(jìn)行加密key的hash。也許一個(gè)聰明的密碼學(xué)家一天就可以找到使用這種攻擊快速破解hash的方法。所以記得使用HMAC。

鹽應(yīng)該拼在密碼的前面還是后面

這個(gè)不重要。選擇一個(gè)并且保持風(fēng)格一致就行了。實(shí)際中,把鹽放在前面更常見(jiàn)一點(diǎn)。

為什么本文最后提供的hash代碼使用了固定執(zhí)行時(shí)間的函數(shù)來(lái)比較hash(length-constant)

使用固定的時(shí)間來(lái)比較hash是為了防止攻擊者在線上的系統(tǒng)中使用基于時(shí)間差的攻擊。這樣攻擊者就只能線下破解了。

比較兩個(gè)字符串是否相同,標(biāo)準(zhǔn)的方式是先比較第一個(gè)字節(jié),然后比較第二個(gè)字節(jié),一次類(lèi)推。只要發(fā)現(xiàn)有一個(gè)字節(jié)不同,那么這兩個(gè)字符串就是不同了??梢苑祷豧alse的消息了。如果所有字節(jié)比較下來(lái)都一樣,那么這兩個(gè)字符串就是相同的,可以返回true。這就意味了比較兩個(gè)字符串,如果他們相同的長(zhǎng)度不一樣,花費(fèi)的時(shí)間不一樣。開(kāi)始部分相同的長(zhǎng)度越長(zhǎng),花費(fèi)的時(shí)間也就越長(zhǎng)。

基于這個(gè)原理,攻擊者可以先找256個(gè)字符串,他們的hash都是以不同的字節(jié)開(kāi)頭。然后發(fā)送到目標(biāo)服務(wù)器,計(jì)算服務(wù)器返回的時(shí)間。時(shí)間最長(zhǎng)的那一個(gè)就是第一個(gè)字節(jié)hash是正確的。依次類(lèi)推。攻擊者就可能得到hash更多的字節(jié)。

這種攻擊聽(tīng)起來(lái)好像在網(wǎng)絡(luò)上實(shí)現(xiàn)起來(lái)比較困難。但是已經(jīng)有人實(shí)現(xiàn)過(guò)了。所以我們?cè)诒容^hash的時(shí)候采用了花費(fèi)時(shí)間固定的函數(shù)。#p#

本文提供的代碼中 slowequals 函數(shù)是怎么工作的

上一回答講到了我們需要比較時(shí)間固定的函數(shù),這部分詳細(xì)講一下代碼的實(shí)現(xiàn)。

1. private static boolean slowEquals(byte[] a, byte[] b)
2. {
3. int diff = a.length ^ b.length;
4. for(int i = 0; i < a.length && i < b.length; i++)
5. diff |= a[i] ^ b[i];
6. return diff == 0;
7. }

這段代碼使用了異或(XOR)操作符”^”來(lái)比較整數(shù)是否相等,而沒(méi)有使用”==”操作符。原因在于如果兩個(gè)數(shù)完全一致,異或之后的值為零。因?yàn)?0 XOR 0 = 0, 1 XOR 1 = 0, 0 XOR 1 = 1, 1 XOR 0 = 1。

所以,第一行代碼如果a.length等于b.length,變量diff等于0,否則的話diff就是一個(gè)非零的值。然后,讓a,b的每一個(gè)字節(jié)XOR之后再跟diff OR。這樣,只有diff一開(kāi)始是0,并且,a,b的每一個(gè)字節(jié)XOR的結(jié)果也是零,最后循環(huán)完成后diff的值才是0,這種情況是a,b完全一樣。否則最后diff是一個(gè)非零的值。

我們使用XOR而不適用”==”的原因是”==”通常編譯成分支的形式。比如C代碼”diff &= a == b” 可能編譯成下面的X86匯編。

MOV EAX, [A]
CMP [B], EAX
JZ equal
JMP done
equal:
AND [VALID], 1
done:
AND [VALID], 0

分支會(huì)導(dǎo)致代碼執(zhí)行的時(shí)間出現(xiàn)差異。

C代碼的”diff |= a ^ b”編譯之后類(lèi)似于,

MOV EAX, [A]
XOR EAX, [B]
OR [DIFF], EAX

執(zhí)行時(shí)間跟兩個(gè)變量是否相等沒(méi)有關(guān)系。

為什么要討論這么多關(guān)于hash的東西

用戶在你的網(wǎng)站上輸入密碼,是相信你的安全性。如果你的數(shù)據(jù)庫(kù)被黑了。而用戶密碼又沒(méi)有恰當(dāng)?shù)谋Wo(hù),那么惡意的攻擊者就可以利用這些密碼嘗試登陸其他的網(wǎng)站和服務(wù)。進(jìn)行撞庫(kù)攻擊。(很多用戶在所有的地方都是使用相同的密碼)這不僅僅是你的網(wǎng)站安全,是你的所有用戶的安全。你要對(duì)你用戶的安全負(fù)責(zé)。#p#

PHP PBKDF2 密碼hash代碼

代碼下載

 
 
 
 
  1. /*
  2.  * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
  3.  * Copyright (c) 2013, Taylor Hornby
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without 
  7.  * modification, are permitted provided that the following conditions are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright notice, 
  10.  * this list of conditions and the following disclaimer.
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright notice,
  13.  * this list of conditions and the following disclaimer in the documentation 
  14.  * and/or other materials provided with the distribution.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  17.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  18.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  19.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
  20.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  21.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  22.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  23.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  24.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  25.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  26.  * POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28. // These constants may be changed without breaking existing hashes.
  29. define("PBKDF2_HASH_ALGORITHM", "sha256");
  30. define("PBKDF2_ITERATIONS", 1000);
  31. define("PBKDF2_SALT_BYTE_SIZE", 24);
  32. define("PBKDF2_HASH_BYTE_SIZE", 24);
  33. define("HASH_SECTIONS", 4);
  34. define("HASH_ALGORITHM_INDEX", 0);
  35. define("HASH_ITERATION_INDEX", 1);
  36. define("HASH_SALT_INDEX", 2);
  37. define("HASH_PBKDF2_INDEX", 3);
  38. function create_hash($password)
  39. {
  40.     // format: algorithm:iterations:salt:hash
  41.     $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
  42.     return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
  43.         base64_encode(pbkdf2(
  44.             PBKDF2_HASH_ALGORITHM,
  45.             $password,
  46.             $salt,
  47.             PBKDF2_ITERATIONS,
  48.             PBKDF2_HASH_BYTE_SIZE,
  49.             true
  50.         ));
  51. }
  52. function validate_password($password, $correct_hash)
  53. {
  54.     $params = explode(":", $correct_hash);
  55.     if(count($params) < HASH_SECTIONS)
  56.        return false;
  57.     $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
  58.     return slow_equals(
  59.         $pbkdf2,
  60.         pbkdf2(
  61.             $params[HASH_ALGORITHM_INDEX],
  62.             $password,
  63.             $params[HASH_SALT_INDEX],
  64.             (int)$params[HASH_ITERATION_INDEX],
  65.             strlen($pbkdf2),
  66.             true
  67.         )
  68.     );
  69. }
  70. // Compares two strings $a and $b in length-constant time.
  71. function slow_equals($a, $b)
  72. {
  73.     $diff = strlen($a) ^ strlen($b);
  74.     for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
  75.     {
  76.         $diff |= ord($a[$i]) ^ ord($b[$i]);
  77.     }
  78.     return $diff === 0;
  79. }
  80. /*
  81.  * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
  82.  * $algorithm - The hash algorithm to use. Recommended: SHA256
  83.  * $password - The password.
  84.  * $salt - A salt that is unique to the password.
  85.  * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
  86.  * $key_length - The length of the derived key in bytes.
  87.  * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
  88.  * Returns: A $key_length-byte key derived from the password and salt.
  89.  *
  90.  * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
  91.  *
  92.  * This implementation of PBKDF2 was originally created by https://defuse.ca
  93.  * With improvements by http://www.variations-of-shadow.com
  94.  */
  95. function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
  96. {
  97.     $algorithm = strtolower($algorithm);
  98.     if(!in_array($algorithm, hash_algos(), true))
  99.         trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
  100.     if($count <= 0 || $key_length <= 0)
  101.         trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);
  102.     if (function_exists("hash_pbkdf2")) {
  103.         // The output length is in NIBBLES (4-bits) if $raw_output is false!
  104.         if (!$raw_output) {
  105.             $key_length = $key_length * 2;
  106.         }
  107.         return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
  108.     }
  109.     $hash_length = strlen(hash($algorithm, "", true));
  110.     $block_count = ceil($key_length / $hash_length);
  111.     $output = "";
  112.     for($i = 1; $i <= $block_count; $i++) {
  113.         // $i encoded as 4 bytes, big endian.
  114.         $last = $salt . pack("N", $i);
  115.         // first iteration
  116.         $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
  117.         // perform the other $count - 1 iterations
  118.         for ($j = 1; $j < $count; $j++) {
  119.             $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
  120.         }
  121.         $output .= $xorsum;
  122.     }
  123.     if($raw_output)
  124.         return substr($output, 0, $key_length);
  125.     else
  126.         return bin2hex(substr($output, 0, $key_length));
  127. }
  128. ?>

#p#

java PBKDF2 密碼hash代碼

代碼下載

 
 
 
 
  1. /* 
  2.  * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
  3.  * Copyright (c) 2013, Taylor Hornby
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without 
  7.  * modification, are permitted provided that the following conditions are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright notice, 
  10.  * this list of conditions and the following disclaimer.
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright notice,
  13.  * this list of conditions and the following disclaimer in the documentation 
  14.  * and/or other materials provided with the distribution.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
  17.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  18.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  19.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
  20.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  21.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
  22.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
  23.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
  24.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  25.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
  26.  * POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28. import java.security.SecureRandom;
  29. import javax.crypto.spec.PBEKeySpec;
  30. import javax.crypto.SecretKeyFactory;
  31. import java.math.BigInteger;
  32. import java.security.NoSuchAlgorithmException;
  33. import java.security.spec.InvalidKeySpecException;
  34. /*
  35.  * PBKDF2 salted password hashing.
  36.  * Author: havoc AT defuse.ca
  37.  * www: http://crackstation.net/hashing-security.htm
  38.  */
  39. public class PasswordHash
  40. {
  41.     public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
  42.     // The following constants may be changed without breaking existing hashes.
  43.     public static final int SALT_BYTE_SIZE = 24;
  44.     public static final int HASH_BYTE_SIZE = 24;
  45.     public static final int PBKDF2_ITERATIONS = 1000;
  46.     public static final int ITERATION_INDEX = 0;
  47.     public static final int SALT_INDEX = 1;
  48.     public static final int PBKDF2_INDEX = 2;
  49.     /**
  50.      * Returns a salted PBKDF2 hash of the password.
  51.      *
  52.      * @param   password    the password to hash
  53.      * @return              a salted PBKDF2 hash of the password
  54.      */
  55.     public static String createHash(String password)
  56.         throws NoSuchAlgorithmException, InvalidKeySpecException
  57.     {
  58.         return createHash(password.toCharArray());
  59.     }
  60.     /**
  61.      * Returns a salted PBKDF2 hash of the password.
  62.      *
  63.      * @param   password    the password to hash
  64.      * @return              a salted PBKDF2 hash of the password
  65.      */
  66.     public static String createHash(char[] password)
  67.         throws NoSuchAlgorithmException, InvalidKeySpecException
  68.     {
  69.         // Generate a random salt
  70.         SecureRandom random = new SecureRandom();
  71.         byte[] salt = new byte[SALT_BYTE_SIZE];
  72.         random.nextBytes(salt);
  73.         // Hash the password
  74.         byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
  75.         // format iterations:salt:hash
  76.         return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" +  toHex(hash);
  77.     }
  78.     /**
  79.      * Validates a password using a&nbs
    分享文章:如何安全的存儲(chǔ)用戶的密碼
    標(biāo)題鏈接:http://m.5511xx.com/article/dhgpgsc.html