# 前言
最近對密碼的加密比較感興趣, 但是對于比較全面的文章, 只找到了這一章, 英文版, 我稍微翻譯了一下, 這里記錄一下
原文:[Salted Password Hashing - Doing it Right](https://crackstation.net/hashing-security.htm)
# 正文
## 序言[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%BA%8F%E8%A8%80)
如果你是一個web開發人員,你可能不得不建立一個用戶帳戶系統。用戶帳戶系統最重要的方面是如何保護用戶密碼。用戶帳戶數據庫經常被黑客入侵,所以如果你的網站被入侵,你絕對必須采取措施保護你用戶的密碼。保護密碼的最好方法是使用salt密碼散列(**salted password hashing**)。本文將解釋為什么它是這樣做的。
關于如何正確地進行密碼散列有很多相互矛盾的想法和誤解,可能是因為網絡上有大量的錯誤信息。密碼散列其實是很簡單的事情之一,但是很多人都會犯錯。通過這一篇文章,我希望不僅解釋正確的方法,而且解釋為什么應該這樣做。
> 重要警告: 如果您正在考慮編寫自己的密碼哈希代碼,**請立即停止**, 這太容易搞砸了。不,你在大學上的密碼學課程并不能使你免除這個警告。這適用于每一個人,**不要編寫自己的 CRYPTO**(**DO NOT WRITE YOUR OWN CRYPTO!**), 存儲密碼的問題已經得到解決, 許多語言都有現成的包和模塊供你使用, 他將比你自己實現的更加完美和穩定
需要說明的是,本篇文章并不是要指導你如何編寫自己的存儲系統,而是要解釋為什么密碼應該以某種方式存儲。
## 什么是密碼散列[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%BB%80%E4%B9%88%E6%98%AF%E5%AF%86%E7%A0%81%E6%95%A3%E5%88%97)
~~~go
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366
hash("waltz") = c0e81794384491161f1777c232bc6bd9ec38f616560b120fda8e90f383853542
~~~
哈希算法是單向函數。他們把任何數量的數據轉換成一個固定長度的“指紋”,不能逆轉。它們還有一個特性,即如果輸入發生一點點的變化,那么產生的散列就完全不同了(參見上面的示例)。這對于保護密碼非常有用,因為我們希望以一種即使密碼文件本身被泄露也能保護密碼的形式存儲密碼,但同時,我們需要能夠驗證用戶的密碼是否正確。
在基于哈希的帳戶系統中,帳戶注冊和身份驗證的一般工作流程如下:
1. 用戶創建一個帳戶
2. 他們的密碼被散列并存儲在數據庫中。任何時候都不會有寫入硬盤的純文本(未加密)密碼
3. 當用戶嘗試登錄時,將根據其真實密碼(從數據庫中檢索)的哈希值與他們輸入的密碼的哈希值進行比對
4. 如果散列匹配,用戶將被授予訪問權限。如果不匹配,用戶將被告知輸入了無效的登錄憑證
5. 當用戶每次登錄時, 重復步驟3和步驟4
在第4步中,千萬不要告訴用戶是不是用戶名或密碼錯了。始終顯示“無效用戶名或密碼”這樣的通用消息。這可防止攻擊者在不知道其密碼的情況下枚舉有效用戶名。
應該注意的是,用于保護密碼的哈希函數與您在數據結構課程中看到的哈希函數不同。用于實現哈希表等數據結構的哈希函數被設計為快速的,而不是安全的。只能使用加密哈希函數來實現密碼哈希。像SHA256、SHA512、RipeMD和WHIRLPOOL這樣的散列函數都是加密散列函數。
很容易認為你所要做的就是通過一個加密散列函數來運行密碼,你的用戶的密碼將是安全的。這與事實相去甚遠。有很多方法可以很快地從普通散列中恢復密碼。有幾種易于實現的技術使這些“攻擊”的效果大大降低。為了激發對這些技術的需求,考慮一下這個網站。在首頁上,您可以提交一個要破解的哈希列表,并在不到一秒鐘的時間內收到結果。顯然,簡單地對密碼進行哈希運算并不能滿足我們對安全性的需求。
## 如何破解哈希[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%A6%82%E4%BD%95%E7%A0%B4%E8%A7%A3%E5%93%88%E5%B8%8C)
### 字典和暴力攻擊[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%AD%97%E5%85%B8%E5%92%8C%E6%9A%B4%E5%8A%9B%E6%94%BB%E5%87%BB)
破解散列的最簡單方法是嘗試猜測密碼,對每個猜測進行散列,并檢查猜測的散列是否等于被破解的散列。如果哈希值相等,則猜測到了密碼。猜測密碼最常見的兩種方法是字典攻擊和暴力攻擊。
字典攻擊使用包含單詞、短語、常用密碼和其他可能用作密碼的字符串的文件。對文件中的每個單詞進行哈希運算,并將其哈希值與密碼哈希值進行比較。如果他們匹配,這個詞就是密碼。這些字典文件是通過從大量文本中提取單詞,甚至從真實的密碼數據庫中提取單詞來構建的。進一步的處理通常應用于字典文件,例如用“leet speak”等價詞替換單詞(“hello”變成“h3110”),以使它們更有效。
暴力攻擊會嘗試所有可能的字符組合,長度不超過給定的長度。這些攻擊在計算上非常昂貴,而且通常在每個處理器時間破解哈希方面效率最低,但它們最終總會找到密碼。密碼應該足夠長,搜索所有可能的字符串以找到它將花費太長的時間是值得的。
我們無法防止字典攻擊或暴力攻擊。它們可以變得不那么有效,但是沒有一種方法可以完全阻止它們。如果密碼散列系統是安全的,破解散列的唯一方法就是對每個散列運行字典或暴力攻擊, 但是對于破解者來說要花費很大的代價和很長的時候才能破解其中一個密碼.
### 查找表[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%9F%A5%E6%89%BE%E8%A1%A8)
查找表是一種非常有效的方法,可以快速地破解許多相同類型的哈希。其基本思想是在密碼字典中預先計算密碼的哈希值,并將其和相應的密碼存儲在查找表數據結構(數據庫)中。使用者提交需要破解的哈希, 由數據庫去查找有沒有對應的密碼, 一個好的查找表實現可以每秒處理數百個哈希查找,即使它們包含數十億個哈希。
如果您想更好地了解查找表的速度,請嘗試使用CrackStation的[免費哈希破解程序](https://crackstation.net/)破解以下sha256哈希(需要FQ)
~~~go
c11083b4b0a7743af748c85d343dfee9fbb8b2576c05f3a7f0d632b0926aadfc
11083b4b0a7743af748c85d343dfe9fbb8b2576c05f3a7f0d632b0926aadfc
08eac03b80adc33dc7d8fbe44b7c7b05d3a2c511166bdb43fcb710b03ba919e7
e4ba5cbd251c98e6cd1c23f126a3b81d8d8328abc95387229850952b3ef9f904
4ba5cbd251c98e6cd1c23f126a3b81d8d8328abc95387229850952b3ef9f904
5206b8b8a996cf5320cb12ca91c7b790fba9f030408efe83ebb83548dc3007bd
~~~
### 反向查找表[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%8F%8D%E5%90%91%E6%9F%A5%E6%89%BE%E8%A1%A8)
此攻擊允許攻擊者對多個哈希同時應用字典或暴力攻擊,而無需預先計算查找表。
首先,攻擊者創建一個查找表,將每個密碼哈希值從受損用戶帳戶數據庫映射到擁有該哈希值的用戶列表。然后,攻擊者對每個密碼猜測進行哈希運算,并使用查找表獲取其密碼是攻擊者猜測的用戶列表。這種攻擊特別有效,因為許多用戶都有相同的密碼。
> 這段比較晦澀, 我使用大白話來解釋一下, 其實是攻擊者注冊一個賬號到被攻擊網站, 然后進入該網站的密碼數據庫, 查找到剛才注冊的帳號的加密后的密碼, 再通過數據庫查找有沒有和該加密密碼一致的加密密碼, 一致則代表某個用戶使用了和你相同的密碼, 即可達到破解的效果
### 彩虹表[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%BD%A9%E8%99%B9%E8%A1%A8)
彩虹表是一種時間記憶折衷技術。它們類似于查尋表,只是它們犧牲了哈希破解的速度,以使查尋表更小。因為它們更小,所以可以將更多散列的解決方案存儲在相同的空間中,從而使它們更有效。彩虹表,可以快速破解任何8個字符生成的md5。
[彩虹表](https://freerainbowtables.com/)
## 加鹽(salt)[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%8A%A0%E7%9B%90salt)
~~~go
hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hello" + "QxLUF1bgIAdeQX") = 9e209040c863f84a31e719795b2577523954739fe5ed3b58a75cff2127075ed1
hash("hello" + "bv5PehSMfV11Cd") = d1d3ec2e6f20fd420d50e2642992841d8338a314b8ea157c9e18477aaef226ab
hash("hello" + "YYLmfY6IehjZMQ") = a49670c3c18b9e079b9cfaf51634f563dc8ae3070db2c4a8544305df1b60f007
~~~
查找表和彩虹表起作用,因為每個密碼的散列方式完全相同。如果兩個用戶有相同的密碼,他們將有相同的密碼哈希。我們可以通過隨機化每個散列來防止這些攻擊,這樣當相同的密碼被散列兩次時,散列就不一樣了。
我們可以通過在散列之前在密碼中添加或預先添加一個隨機字符串(稱為**salt**)來隨機散列。如上面的示例所示,每次都會將相同的密碼散列成完全不同的字符串。為了檢查密碼是否正確,我們需要這個密碼加密時的salt,因此它通常與散列一起存儲在用戶帳戶數據庫中,或者作為散列字符串本身的一部分。
Salt不需要加密。因為salt是隨機的, 隨機化了哈希值,查找表、反向查找表和彩虹表就變得無效了。攻擊者不會預先知道salt是什么,因此無法預先計算查找表或彩虹表。如果每個用戶的密碼都用不同的salt散列,那么反向查找表攻擊也不會起作用。
## 加鹽的錯誤使用[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%8A%A0%E7%9B%90%E7%9A%84%E9%94%99%E8%AF%AF%E4%BD%BF%E7%94%A8)
### 重復使用一個鹽[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E9%87%8D%E5%A4%8D%E4%BD%BF%E7%94%A8%E4%B8%80%E4%B8%AA%E7%9B%90)
一個常見的錯誤是在每個散列中使用相同的鹽。salt要么硬編碼到程序中,要么隨機生成一次。這是無效的,因為如果兩個用戶有相同的密碼,他們仍然會有相同的哈希。攻擊者仍然可以使用反向查找表攻擊同時對每個哈希運行字典攻擊。他們只需對每個密碼猜測應用salt,然后再對其進行哈希運算。如果將salt硬編碼到一個流行的產品中,那么可以為該salt構建查找表和彩虹表,以便更容易地破解產品生成的哈希。
> 每次用戶創建帳戶或更改密碼時,都必須生成一個新的隨機salt。
### 短鹽[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E7%9F%AD%E7%9B%90)
如果鹽太短,攻擊者可以為每個可能的鹽建立一個查找表。例如,如果salt只有三個ASCII字符,則只有95x95x95=857375個可能的salt。這看起來可能很多,但是如果每個查找表只包含1MB最常用的密碼,那么它們總共只有837GB,考慮到現在1000 GB的硬盤售價低于100美元,這并不算多。
出于同樣的原因,用戶名不應該用作salt。用戶名對于單個服務可能是唯一的,但是它們是可預測的,并且經常被其他服務上的帳戶重用。攻擊者可以為常見用戶名構建查找表,并使用它們來破解用戶名作為salt的哈希。
> 為了使攻擊者無法為每個可能的salt創建一個查找表,salt必須很長。一個好的經驗法則是使用與哈希函數輸出大小相同的salt。例如,SHA256的輸出是256位(32字節),因此salt應該至少是32個隨機字節。
## 散列算法的錯誤使用[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%95%A3%E5%88%97%E7%AE%97%E6%B3%95%E7%9A%84%E9%94%99%E8%AF%AF%E4%BD%BF%E7%94%A8)
本節介紹另一個常見的密碼散列誤解:散列算法的古怪組合。人們很容易忘乎所以地嘗試組合不同的散列函數,希望結果更安全。但實際上,這樣做并沒有什么好處。它只會造成互操作性問題,有時甚至會降低哈希的安全性。永遠不要試圖發明你自己的密碼,總是使用一個由專家設計的標準。有些人會認為使用多個散列函數會使計算散列的過程變慢,所以破解會變慢,但是有一個更好的方法可以讓破解過程變慢,我們稍后會看到。
下面是一些我在互聯網論壇上看到的糟糕的古怪哈希函數的例子
* 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)))
不要使用這些。
注:本節已被證明是有爭議的。我收到過很多郵件,認為古怪的散列函數是件好事,因為如果攻擊者不知道使用的是哪一個散列函數就更好了,攻擊者為古怪的散列函數預先計算彩虹表的可能性更小,計算散列函數的時間也更長。
攻擊者在不知道算法的情況下無法攻擊哈希,但請注意[Kerckhoffs](https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle)的原理,即攻擊者通常可以訪問源代碼(尤其是免費或開源軟件),并且給定目標系統中的一些密碼哈希對,對算法進行反向工程并不困難。計算古怪的散列函數確實需要更長的時間,但只需要一個小的常數因子。最好使用一個設計得非常難以并行化的迭代算法(這些將在下面討論)。而且,適當地對散列進行鹽分可以解決彩虹表問題。
如果你真的想使用一個標準化的“古怪”散列函數,比如HMAC,那就沒關系了。但是,如果這樣做是為了降低哈希計算的速度,請先閱讀下面關于密鑰擴展(**key stretching**)的部分。
將這些小的好處與意外實現完全不安全的散列函數的風險以及古怪的散列所產生的互操作性問題進行比較。顯然,最好使用標準的、經過良好測試的算法。
## 散列碰撞[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%95%A3%E5%88%97%E7%A2%B0%E6%92%9E)
因為散列函數將任意數量的數據映射到固定長度的字符串,所以必須有一些輸入散列到相同的字符串中。加密散列函數的設計使這些沖突難以找到。有時,密碼學家會發現對散列函數的“攻擊”,從而更容易找到沖突。最近的一個例子是MD5散列函數,實際上已經找到了它的沖突。
> 就是有可能字符串 abc 和 bbc 生成的散列是相同的
沖突攻擊是一種跡象,表明除用戶密碼以外的字符串更有可能具有相同的哈希值。然而,即使是在MD5這樣的弱哈希函數中發現沖突,也需要大量專用的計算能力,因此在實踐中,這些沖突“偶然”發生的可能性非常小。使用MD5和salt散列的密碼在所有實際用途中都是安全的,就像使用SHA256和salt散列一樣。不過,如果可能的話,最好使用更安全的散列函數,如SHA256、SHA512、RipeMD或WHIRLPOOL。
## 如何正確的散列[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E6%95%A3%E5%88%97)
本節詳細描述了密碼應該如何散列。第一小節涵蓋了基本的一切,這是絕對必要的。下面的小節將解釋如何擴充基礎知識,使哈希更難破解。
### 用鹽做散列[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E7%94%A8%E7%9B%90%E5%81%9A%E6%95%A3%E5%88%97)
警告:不要只閱讀本節。您絕對必須實現下一節中的內容:“使密碼破解更困難:慢哈希函數”
我們已經看到惡意黑客如何使用查找表和彩虹表快速破解普通哈希。我們已經了解到使用salt隨機散列是問題的解決方案。但是如何生成salt,以及如何將其應用于密碼?
Salt應該使用加密安全的偽隨機數生成器(**CSPRNG**)生成。csprng與普通的偽隨機數生成器非常不同,比如“C”語言的rand()函數。顧名思義,csprng被設計成密碼安全的,這意味著它們提供了高度的隨機性,并且完全不可預測。我們不希望鹽是可預測的,所以我們必須使用CSPRNG。每個語言都有對應的函數供你使用, 例如 python 的`os.urandom`等
每個用戶每個密碼的salt必須是唯一的。每次用戶創建帳戶或更改密碼時,都應該使用新的隨機salt對密碼進行哈希運算。不要重復使用鹽。鹽也應該足夠長, 以保證有足夠多的鹽用于哈希加密。根據經驗,使salt至少與哈希函數的輸出一樣長。salt應該與hash一起存儲在用戶賬戶表中。
### 保存密碼[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%BF%9D%E5%AD%98%E5%AF%86%E7%A0%81)
1. 使用 CSPRNG 生成足夠長的隨機鹽
2. 將salt預先混入密碼中,并使用標準密碼哈希函數(如Argon2、bcrypt、scrypt或PBKDF2)對其進行哈希運算。
3. 在用戶的數據庫記錄中保存salt和hash。
### 校驗密碼[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%A0%A1%E9%AA%8C%E5%AF%86%E7%A0%81)
1. 從數據庫中檢索用戶的salt和hash。
2. 將salt混入用戶提交的密碼,并使用相同的哈希函數對其進行哈希運算。
3. 將給定密碼的哈希值與數據庫中的哈希值進行比較。如果匹配,則密碼正確。否則,密碼不正確。
### 在 Web 應用程序中,始終在服務器上進行散列[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%9C%A8-web-%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E4%B8%AD%E5%A7%8B%E7%BB%88%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E8%BF%9B%E8%A1%8C%E6%95%A3%E5%88%97)
如果您正在編寫一個web應用程序,您可能想知道在哪里散列。密碼應該在用戶瀏覽器中用JavaScript散列,還是應該發送將明文密碼發送到服務器再進行散列呢?
即使用JavaScript散列用戶密碼,也必須在服務器上散列。考慮一個網站,它在用戶瀏覽器中散列用戶密碼,而不在服務器上散列。若要驗證用戶身份,此網站將接受來自瀏覽器的哈希值,并檢查該哈希值是否與數據庫中的哈希值完全匹配。這似乎比在服務器上散列更安全,因為用戶的密碼從未發送到服務器,但事實并非如此。
問題是客戶端的哈希值邏輯上變成了用戶的密碼。用戶需要做的只是告訴服務器他們密碼的散列。如果一個壞人得到了一個用戶的散列,他們可以用它來驗證服務器,而不知道用戶的密碼!**因此,如果壞人不知何故從這個假想的網站竊取了哈希數據庫,他們就可以立即訪問每個人的帳戶,而不必猜測任何密碼**。
這并不是說你不應該在瀏覽器中散列,但如果你這樣做了,你絕對也必須在服務器上散列。在瀏覽器中進行散列當然是一個好主意,但在實現時請考慮以下幾點:
* 客戶端密碼哈希不能替代HTTPS(SSL/TLS)。如果瀏覽器和服務器之間的連接不安全,中間人可以在下載JavaScript代碼時修改它,以刪除散列功能并獲取用戶的密碼。
* 有些web瀏覽器不支持JavaScript,有些用戶在瀏覽器中禁用JavaScript。因此,為了獲得最大的兼容性,你的應用程序應該檢測瀏覽器是否支持JavaScript,如果不支持,就在服務器上模擬客戶端散列。
* 你也需要在客戶端散列的過程中中加鹽。顯而易見的解決方案是讓客戶端腳本向服務器請求用戶的salt。不要這樣做,因為它讓攻擊者在不知道密碼的情況下檢查用戶名是否有效。由于您也在服務器上進行散列和加鹽(使用一個合格的salt),因此可以使用用戶名(或電子郵件)與特定于站點的字符串(例如域名)連接作為客戶端salt(這個客戶端salt并不是最終的salt, 客戶端生成的加密密碼也不是最終的密碼, 只是為了將明文密碼加密而防止中間人攻擊, 并且,當黑客拿到了客戶端加密后的密碼, 他也能通過請求登錄接口模擬客戶端已經加密密碼然后登錄的情況, 此時則與客戶端不加密的效果一致)。
### 使得密碼破解更難: 緩慢的散列函數[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%BD%BF%E5%BE%97%E5%AF%86%E7%A0%81%E7%A0%B4%E8%A7%A3%E6%9B%B4%E9%9A%BE-%E7%BC%93%E6%85%A2%E7%9A%84%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B0)
Salt確保攻擊者不能使用專門的攻擊,如查找表和彩虹表來快速破解大量散列集合,但它不能阻止攻擊者單獨對每個散列運行字典或暴力攻擊。高端圖形卡(GPU)和定制硬件每秒可以計算數十億個哈希,因此這些攻擊仍然非常有效。為了降低這些攻擊的效果,我們可以使用一種稱為密鑰擴展(**key stretching**)的技術。
其想法是使哈希函數非常慢,因此即使使用快速的GPU或定制硬件,字典和暴力攻擊也很慢,從而讓攻擊者放棄或者失敗。目標是使散列函數足夠慢以阻止攻擊,但仍然足夠快以避免給用戶造成明顯的延遲。
密鑰擴展是使用一種特殊類型的CPU密集型哈希函數實現的。不要試圖創造你自己的加密函數, 簡單地對密碼的散列進行迭代散列是不夠的,因為它可以在硬件中并行化,并且可以像普通散列一樣快速執行。使用標準算法,如[PBKDF2](https://en.wikipedia.org/wiki/PBKDF2)或[bcrypt](https://en.wikipedia.org/wiki/Bcrypt)。
這些算法以安全因子或迭代計數作為參數。此值確定哈希函數的速度。對于桌面軟件或智能手機應用程序,選擇此參數的最佳方法是在設備上運行一個簡短的基準測試,以找到計算哈希值的耗時約半秒的參數。這樣,您的程序就可以在不影響用戶體驗的情況下盡可能安全。
如果您在web應用程序中使用密鑰擴展哈希,請注意您將需要額外的計算資源來處理大量身份驗證請求,并且密鑰擴展可能會使您的網站更容易受到拒絕服務(DoS)攻擊。我仍然建議使用密鑰擴展,但是迭代次數要少一些。您應該根據計算資源和預期的最大身份驗證請求速率來設置迭代次數。通過讓用戶在每次登錄時輸入驗證碼,可以消除DoS威脅。系統在設計時要將迭代次數設置為可配置的,以便將來可以增加或減少迭代次數。
如果您擔心計算負擔,但仍希望在web應用程序中使用密鑰擴展,請考慮使用JavaScript在用戶瀏覽器中運行密鑰擴展算法。[Stanford Javascript Crypto Library](http://bitwiseshiftleft.github.io/sjcl/)加密庫包括PBKDF2。迭代次數應該設置得足夠低,這樣系統就可以在移動設備等速度較慢的客戶端上使用,如果用戶的瀏覽器不支持JavaScript,那么系統應該返回到服務器端來模擬客戶端計算。客戶端密鑰擴展并不能消除服務器端散列的需要。你必須對客戶端傳回來的哈希值再次進行哈希加密, 也就是說客戶端生成的加密密碼并不是最終的加密密碼, 以此來增加安全性, 因為前端的代碼是透明的, 不能將完整的算法放置在前端
### 不可能破解的哈希: 密鑰哈希和密碼哈希設備[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%B8%8D%E5%8F%AF%E8%83%BD%E7%A0%B4%E8%A7%A3%E7%9A%84%E5%93%88%E5%B8%8C-%E5%AF%86%E9%92%A5%E5%93%88%E5%B8%8C%E5%92%8C%E5%AF%86%E7%A0%81%E5%93%88%E5%B8%8C%E8%AE%BE%E5%A4%87)
只要攻擊者可以使用哈希來檢查密碼猜測是否正確,他們就可以對哈希運行字典或暴力攻擊。下一步是將密鑰添加到哈希中,這樣只有知道密鑰的人才能使用哈希來驗證密碼。這可以通過兩種方式實現。可以使用類似AES的算法對散列進行加密,或者使用類似[HMAC](https://en.wikipedia.org/wiki/HMAC)的密鑰散列算法將密鑰包括在散列中。
這并不像聽起來那么容易。密鑰必須對攻擊者保密,即使在出現漏洞的情況下也是如此。如果攻擊者獲得對系統的完全訪問權限,則無論密鑰存儲在何處,他們都可以竊取密鑰。密鑰必須存儲在外部系統中,例如專門用于密碼驗證的物理上獨立的服務器,或者連接到服務器的特殊硬件設備,例如[YubiHSM](https://www.yubico.com/YubiHSM)。
對于任何大規模(超過10萬個用戶)的服務,我強烈推薦這種方法。我認為有必要為任何托管超過100萬個用戶帳戶使用該方法。
如果您負擔不起多個專用服務器或特殊硬件設備,您仍然可以在標準web服務器上獲得密鑰哈希的一些好處。大多數數據庫都是通過SQL注入攻擊被破壞的,在大多數情況下,SQL注入攻擊不允許攻擊者訪問本地文件系統(如果SQL server具有此功能,則禁用對本地文件系統的訪問)。如果生成一個隨機密鑰并將其存儲在無法從web訪問的文件中,并將其包含到salt哈希中,那么如果使用簡單的SQL注入攻擊破壞數據庫,哈希就不會受到攻擊。不要將密鑰硬編碼到源代碼中,而是在安裝應用程序時隨機生成密鑰。這不如使用單獨的系統進行密碼的管理安全,因為如果web應用程序中存在SQL注入漏洞,攻擊者可能會使用其他類型(如本地文件包含)來讀取密鑰文件。但是,總比什么都沒有強。
請注意,密鑰哈希并不能消除對鹽的需要。聰明的攻擊者最終會找到破解密鑰的方法,因此密碼哈希依然需要加鹽和密鑰擴展, 這是很重要的。
### 其他安全措施[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%85%B6%E4%BB%96%E5%AE%89%E5%85%A8%E6%8E%AA%E6%96%BD)
密碼哈希在發生安全漏洞時保護密碼。它并不能使整個應用程序更加安全。為了防止密碼散列(和其他用戶數據)在第一時間被竊取,還必須做更多的工作。
即使是有經驗的開發人員也必須接受安全教育,才能編寫安全的應用程序。了解web應用程序漏洞的一個重要資源是openweb應用程序安全項目[OWASP](https://owasp.org/)。一個很好的介紹是[OWASP十大漏洞列表](https://owasp.org/www-project-proactive-controls/)。除非您了解列表中的所有漏洞,否則不要嘗試編寫處理敏感數據的web應用程序。雇主有責任確保所有開發人員都接受過安全應用程序開發方面的充分培訓。
對您的應用程序進行第三方"滲透測試"是一個好主意。即使是最好的程序員也會犯錯,因此讓安全專家檢查代碼中的潛在漏洞總是有意義的。找一個值得信賴的組織(或雇傭員工)定期檢查您的代碼。安全審查過程應該在應用程序生命周期的早期開始,并在整個開發過程中持續進行。
同樣重要的是要監測你的網站,以發現入侵行為。我建議至少雇傭一個全職工作是檢測和處理安全漏洞的人。如果一個漏洞未被發現,攻擊者可以通過你的網站利用惡意軟件感染訪問者,所以檢測漏洞并及時作出反應是非常重要的。
## 常見問題[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
### 我應該使用什么散列算法?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%88%91%E5%BA%94%E8%AF%A5%E4%BD%BF%E7%94%A8%E4%BB%80%E4%B9%88%E6%95%A3%E5%88%97%E7%AE%97%E6%B3%95)
**使用**
* 精心設計并經過大量測試的密鑰擴展算法比如[PBKDF2](https://en.wikipedia.org/wiki/PBKDF2),[Bcrypt](https://en.wikipedia.org/wiki/Bcrypt),[Scrypt](http://www.tarsnap.com/scrypt.html)
* 很好的開源項目比如[Portable PHP password hashing framework](https://www.openwall.com/phpass/)
* PBKDF2在各個語言的實現
* [crypt](https://en.wikipedia.org/wiki/Crypt_%28Unix%29#Library_Function_crypt.283.29)的安全版本
**不要使用**
* 快速密碼散列函數,如 MD5、 SHA1、 SHA256、 SHA512、 RipeMD、 WHIRLPOOL、 SHA3等
* 不安全版本的 crypt
* 任何你自己設計的算法。只能使用公共領域的技術,并且經過經驗豐富的密碼學家的良好測試。
### 當用戶忘記密碼時,我應該如何允許他們重置密碼?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%BD%93%E7%94%A8%E6%88%B7%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81%E6%97%B6%E6%88%91%E5%BA%94%E8%AF%A5%E5%A6%82%E4%BD%95%E5%85%81%E8%AE%B8%E4%BB%96%E4%BB%AC%E9%87%8D%E7%BD%AE%E5%AF%86%E7%A0%81)
我個人認為,現在廣泛使用的所有密碼重置機制都是不安全的。如果你有很高的安全要求,比如加密服務,不要讓用戶重置密碼。
大多數網站使用向忘記密碼的用戶發送郵件來驗證身份。為此,生成一個與帳戶緊密關聯的隨機一次性令牌(Token)。將其包含在發送到用戶電子郵件地址的密碼重置鏈接中。當用戶單擊包含有效令牌的密碼重置鏈接時,提示他們輸入新密碼。確保令牌與用戶帳戶緊密綁定,以便攻擊者不能使用發送到自己電子郵件地址的令牌重置其他用戶的密碼。
令牌必須設置為在15分鐘內或使用后過期,一旦使用過就立即作廢。當用戶登錄(他們想起來了他們的密碼)或請求一個新的重置密碼時,使該用戶任何現有的密碼令牌過期。如果一個令牌沒有過期,它可以永遠被用來入侵用戶的帳戶。電子郵件(SMTP)是一種純文本協議,互聯網上存在截取電子郵件的惡意路由。用戶的電子郵件帳戶(包括重置鏈接)可能會在其修改密碼后很長一段時間內被泄露出去。而使令牌盡快過期可以減少用戶將信息暴露給攻擊者的風險。
攻擊者將能夠修改令牌,因此不要在其中存儲用戶帳戶信息或失效時間。它們應該是不可猜測的隨機二進制數據,僅用于標識數據庫表中的某條用戶記錄。
千萬不要通過電子郵件向用戶發送新密碼。當用戶重置密碼時,請記住選擇一個新的隨機鹽。不要重復使用他們的舊鹽。
### 如果我的用戶帳戶資料庫被泄漏/入侵,我應該怎么辦?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%A6%82%E6%9E%9C%E6%88%91%E7%9A%84%E7%94%A8%E6%88%B7%E5%B8%90%E6%88%B7%E8%B5%84%E6%96%99%E5%BA%93%E8%A2%AB%E6%B3%84%E6%BC%8F%E5%85%A5%E4%BE%B5%E6%88%91%E5%BA%94%E8%AF%A5%E6%80%8E%E4%B9%88%E5%8A%9E)
您的首要任務是確定系統是如何受到危害的,并修補攻擊者用來進入的漏洞。如果你沒有應對漏洞的經驗,我強烈建議你聘請第三方安全公司。
可能會有人試圖掩蓋漏洞,希望沒人注意到。然而,試圖掩蓋漏洞會讓你變得更糟,因為你沒有通知用戶密碼和其他個人信息可能會被泄露,這會讓你的用戶面臨更大的風險。你必須盡快通知你的用戶,即使你還沒有完全明白發生了什么。在你的網站首頁放一個通知,鏈接到一個有更詳細信息的頁面,如果可能的話,通過電子郵件向每個用戶發送一個通知。
向你的用戶解釋他們的密碼是如何被鹽哈希保護的,即使他們被鹽哈希保護,惡意黑客仍然可以對哈希運行字典和暴力攻擊。惡意黑客會使用他們找到的任何密碼試圖登錄到另一個網站上的用戶帳戶,希望他們在兩個網站上使用相同的密碼。將此風險告知您的用戶,并建議他們在使用類似密碼的任何網站或服務上更改密碼。強制他們在下次登錄時更改您服務的密碼。大多數用戶會嘗試將自己的密碼“更改”為原始密碼,以快速繞過強制更改。你需要使用當前密碼散列來校驗新舊密碼是否相同來確保他們不能這樣做。
很可能,即使使用鹽哈希,攻擊者也能很快破解一些弱密碼。為了減少攻擊者使用這些密碼的機會,在登錄時除了當前密碼之外,還應該要求通過電子郵件進行身份驗證,直到用戶更改了密碼。請參閱前面的問題,“當用戶忘記密碼時,我應該如何允許用戶重置密碼?”有關實現電子郵件驗證身份的提示。
同時告訴你的用戶網站上儲存了什么樣的個人信息。如果你的數據庫包含信用卡號碼,你應該指示你的用戶仔細檢查他們最近和將來的賬單,并注銷他們的信用卡。
### 我的密碼政策應該是什么? 我應該強制使用強密碼嗎?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%88%91%E7%9A%84%E5%AF%86%E7%A0%81%E6%94%BF%E7%AD%96%E5%BA%94%E8%AF%A5%E6%98%AF%E4%BB%80%E4%B9%88-%E6%88%91%E5%BA%94%E8%AF%A5%E5%BC%BA%E5%88%B6%E4%BD%BF%E7%94%A8%E5%BC%BA%E5%AF%86%E7%A0%81%E5%90%97)
如果您的服務沒有嚴格的安全要求,那么不要限制您的用戶。我建議在用戶鍵入密碼時向其顯示有關密碼強度的信息,讓他們決定密碼的安全性。如果您有特殊的安全需求,請強制執行至少12個字符的長度,并且至少需要兩個字母、兩個數字和兩個符號。
不要強迫用戶每六個月更改一次以上的密碼,因為這樣做會造成“用戶疲勞”,使用戶不選擇復雜的密碼。相反,教育用戶在感到密碼被泄露時主動更改密碼,并且永遠不要把密碼告訴任何人。如果是商業環境,鼓勵員工利用帶薪時間熟記和使用密碼。
### 如果攻擊者可以訪問我的數據庫,難道他們不能用自己的散列和登錄來替換密碼的散列嗎?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E5%A6%82%E6%9E%9C%E6%94%BB%E5%87%BB%E8%80%85%E5%8F%AF%E4%BB%A5%E8%AE%BF%E9%97%AE%E6%88%91%E7%9A%84%E6%95%B0%E6%8D%AE%E5%BA%93%E9%9A%BE%E9%81%93%E4%BB%96%E4%BB%AC%E4%B8%8D%E8%83%BD%E7%94%A8%E8%87%AA%E5%B7%B1%E7%9A%84%E6%95%A3%E5%88%97%E5%92%8C%E7%99%BB%E5%BD%95%E6%9D%A5%E6%9B%BF%E6%8D%A2%E5%AF%86%E7%A0%81%E7%9A%84%E6%95%A3%E5%88%97%E5%90%97)
是的,但是如果有人可以訪問你的數據庫,他們可能已經可以訪問你服務器上的所有內容,所以他們不需要登錄到你的帳戶就可以得到他們想要的。密碼散列(在網站中)的目的不是保護網站不被破壞,而是在確實發生入侵時保護數據庫中的密碼。
通過使用兩個具有不同權限的用戶連接到數據庫,可以防止哈希在SQL注入攻擊期間被替換。一個用于“創建帳戶”代碼,另一個用于“登錄”代碼。“create account”代碼操作的用戶應該能夠讀取和寫入用戶表,但是“login”代碼使用的用戶應該只能讀取。
### 為什么我必須使用像 HMAC 這樣的特殊算法?為什么我不能把密碼附加到密鑰上呢?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8%E5%83%8F-hmac-%E8%BF%99%E6%A0%B7%E7%9A%84%E7%89%B9%E6%AE%8A%E7%AE%97%E6%B3%95%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E4%B8%8D%E8%83%BD%E6%8A%8A%E5%AF%86%E7%A0%81%E9%99%84%E5%8A%A0%E5%88%B0%E5%AF%86%E9%92%A5%E4%B8%8A%E5%91%A2)
像 MD5、 SHA1和 SHA2這樣的散列函數使用[Merkle-Damg?rd](https://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_construction)結構,這使它們容易受到所謂的長度擴展攻擊(length extension attack)。這意味著給定一個 哈希 h (x) ,對于任意的字符串 Y,攻擊者可以計算出 h(pad(X)+Y) 的值,而無需知道 X 的值。其中, pad(X) 是哈希函數的填充函數。
這意味著,攻擊者不知道密鑰的情況下,仍然可以根據給定的哈希值 H(key+message) 計算出 H(pad(key+message)+extension) 。如果該哈希值用于身份認證,并依靠其中的密鑰來防止攻擊者篡改消息,這方法已經行不通。因為攻擊者無需知道密鑰也能構造出包含 message+extension 的一個有效的哈希值。
目前尚不清楚攻擊者如何利用此攻擊更快地破解密碼哈希。但是,由于受到攻擊,使用普通哈希函數進行密鑰哈希加密被認為是不好的做法。一個聰明的密碼學家也許有一天會想出一個聰明的方法來使用這些攻擊來加快破解速度,所以還是使用HMAC 較好。
### 鹽應該放在密碼的前面還是后面?[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E7%9B%90%E5%BA%94%E8%AF%A5%E6%94%BE%E5%9C%A8%E5%AF%86%E7%A0%81%E7%9A%84%E5%89%8D%E9%9D%A2%E8%BF%98%E6%98%AF%E5%90%8E%E9%9D%A2)
這不重要,但是為了互操作性,選擇一個并保持風格一致即可。把鹽放在密碼之前似乎更為常見。
### 需要保證對比密碼時的操作時間相同[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E9%9C%80%E8%A6%81%E4%BF%9D%E8%AF%81%E5%AF%B9%E6%AF%94%E5%AF%86%E7%A0%81%E6%97%B6%E7%9A%84%E6%93%8D%E4%BD%9C%E6%97%B6%E9%97%B4%E7%9B%B8%E5%90%8C)
> 原文這里很晦澀, 理解了一會才明白是什么意思
>
> 標題的意思是, 假設用戶的密碼哈希是 'abcde', 當服務端接收到了用戶提交的密碼, 哈希后假如是 'adcca' 或者是 '121333' 或者是其他的, 要保證判斷他們與數據庫中的是否一致的代碼在處理所有密碼時都要有一致的處理時間
比較哈希值的函數遵循 "時間一致" 可確保攻擊者無法在使用計時攻擊的在線系統中提取密碼的哈希值,然后離線破解密碼。
檢查兩個字節(字符串)是否相同的標準方法是比較第一個字節、第二個字節、第三個字節,依此類推。一旦發現兩個字符串的字節不一樣,就知道它們是不同的,可以立即返回false。如果您在兩個字符串中都沒有找到任何不同的字節,那么您就知道字符串是相同的,并且可以返回一個true。這意味著比較兩個字符串可能需要不同的時間,具體取決于匹配的字符串數量。
例如,對字符串“xyzabc”和“abcxyz”進行標準比較,可以立即看到第一個字符是不同的,不必檢查字符串的其余部分。另一方面,當比較字符串“aaaaaaaab”和“aaaaaaaaaaz”時,比較算法在掃描到 z 時才會確認這兩個字符串是不一樣的, 這代表他已經檢查了前面幾位, 顯而易見的, 一般的, 比較算法在處理這兩個判斷時用時是不一樣的, 后面的會比前面的處理慢一些。
假設攻擊者想要侵入一個在線系統,該系統將身份驗證嘗試的速率限制為每秒一次。另外,假設攻擊者知道密碼散列的所有參數(salt、散列類型等),但密碼和散列后的密碼除外。如果攻擊者能夠精確測量在線系統將真實密碼的散列值與攻擊者提供的密碼的散列值進行比較所需的時間,那么他可以使用定時攻擊提取部分散列值,并使用脫機攻擊進行破解,從而繞過系統的速率限制。
首先,攻擊者找到256個字符串,其哈希值以每個可能的字節開始。他將每個字符串發送到在線系統,記錄系統響應所需的時間。耗時最長的字符串將是哈希的第一個字節與實際哈希的第一個字節匹配的字符串。攻擊者現在知道第一個字節,可以以類似的方式繼續攻擊第二個字節,然后攻擊第三個字節,依此類推。一旦攻擊者對散列有足夠的了解,他就可以使用自己的硬件來破解它,而不受系統的速率限制。
> 這里指的是: 我不知道你的密碼, 但是密碼的每一位肯定是在256個字符串中間的某一個, 假如代碼不遵循 '操作時間相同' 原則, 那我試這256次, 耗時最長的那一個肯定是第一位是對的, 然后我試第二個字符, 一直到我試出來密碼或者我根據試出來的猜測出密碼
在網絡上運行定時攻擊似乎是不可能的, 因為網絡本身具有延遲。然而,[有人](https://crypto.stanford.edu/~dabo/papers/ssl-timing.pdf)已經做到了,并且被證明是切實可行的。這就是為什么要遵循 '操作時間相同' 原則
### 怎么編寫遵循 '操作時間相同' 的代碼[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E6%80%8E%E4%B9%88%E7%BC%96%E5%86%99%E9%81%B5%E5%BE%AA-%E6%93%8D%E4%BD%9C%E6%97%B6%E9%97%B4%E7%9B%B8%E5%90%8C-%E7%9A%84%E4%BB%A3%E7%A0%81)
前面的問題解釋了為什么操作時間相同是必要的,這里解釋了代碼實際上是如何工作的。
~~~go
private static boolean slowEquals(byte[] a, byte[] b)
{
int diff = a.length ^ b.length;
for(int i = 0; i < a.length && i < b.length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
}
~~~
代碼使用 XOR“ ^”運算符來比較整數是否相等,而不是使用“ = =”運算符。原因如下。當且僅當兩個整數完全相同時,XORing 的結果才為0。這是因為0 XOR 0 = 0,1 XOR 1 = 0,0 XOR 1 = 1,1 XOR 0 = 1。如果我們對兩個整數中的所有位都應用這個函數,那么只有當所有位都匹配時,結果才是0。
所以, 如上面代碼所示, 先判斷這兩個哈希值的長度, 如果其不一致, diff為 1, 但是并不直接退出, 而是在進行 for 循環, 結束后統一返回, 也就是說, 就算在代碼中能夠提前判斷不正確也不立刻返回而是同樣遍歷一遍達到耗時相同的效果
我們需要使用XOR而不是“= =”運算符來比較整數的原因是“= =”通常被翻譯/編譯/解釋為一個branch。例如,C代碼“”可能編譯為以下x86程序集:diff &= a == b
~~~go
MOV EAX, [A]
CMP [B], EAX
JZ equal
JMP done
equal:
AND [VALID], 1
done:
AND [VALID], 0
~~~
分支使代碼根據整數的相等性和CPU的內部分支預測狀態以不同的時間量執行。
C代碼應編譯為以下內容,其執行時間不依賴于整數的相等性:diff |= a ^ b
~~~go
MOV EAX, [A]
XOR EAX, [B]
OR [DIFF], EAX
~~~
### 為什么要為你的網站設置密碼加密[#](https://www.cnblogs.com/chnmig/p/14475648.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%B8%BA%E4%BD%A0%E7%9A%84%E7%BD%91%E7%AB%99%E8%AE%BE%E7%BD%AE%E5%AF%86%E7%A0%81%E5%8A%A0%E5%AF%86)
您的用戶正在您的網站中輸入密碼。證明他們相信你的安全。
如果你的數據庫遭到黑客攻擊,而你的用戶密碼沒有受到保護,那么惡意黑客就可以利用這些密碼危害你在其他網站和服務上的用戶帳戶(大多數人在任何地方都使用相同的密碼)。
風險不僅僅在于你的安全,還在于你的用戶。
**你要對你的用戶的安全負責**
- php開發
- 常用技巧
- 字符數組對象
- php換行替換,PHP替換回車換行符的三種方法
- PHP 數組轉字符串,與字符串轉數組
- php將img中的寬高刪除,PHP刪除HTML中寬高樣式的詳解
- php去除換行(回車換行)的三種方法
- php 過濾word 樣式
- php如何設置隨機數
- 2個比較經典的PHP加密解密函數分享
- php怎么去除小數點后多余的0
- php中判斷是一維數組還是二維數組的解決方案
- php 獲取數組中出現次數最多的值(重復最多的值)與出現的次數
- PHP過濾掉換行符、特殊空格、制表符等
- PHP中json_endoce轉義反斜杠的問題
- PHP過濾Emoji表情和特殊符號的方法
- PHP完美的提取鏈接正則
- php很牛的圖片采集
- 日期處理
- php 獲取今日、昨日、上周、本月的起始時間戳和結束時間戳的方法非常簡單
- PHP指定時間戳/日期加一天,一年,一周,一月
- 使用php 獲取時間今天明天昨天時間戳的詳解
- php獲得當月的節假日函數(包含周末,年度節假日)
- PHP獲取本月起始和截止時間戳
- php 獲取每月開始結束時間,php 獲取指定月份的開始結束時間戳
- PHP獲取今天,昨天,本月,上個月,本年 起始時間戳
- php、mysql查詢當天,本周,本月的用法
- php獲取兩個時間戳之間相隔多少天多少小時多少分多少秒
- 毫秒級時間戳和日期格式轉換
- php-倒計時
- 請求提交上傳
- php+put+post,Curl和PHP-如何通過PUT,POST,GET通過curl傳遞json
- PHP put提交和獲取數據
- PHP curl put方式上傳文件
- 數據導入導出
- PHP快速導入大量數據到數據庫的方法
- PHP快速導出百萬級數據到CSV或者EXCEL文件
- PHP解析大型Excel表格的庫:box/spout
- PHP導入(百萬級)Excel表格數據
- PHP如何切割excel大文件
- 使用 PHP_XLSXWriter 代替 PHPExcel 10W+ 數據秒級導出
- 安裝php擴展XLSXWriter
- 解決php導入excel表格時獲取日期變成浮點數的方法
- xml處理
- PHP XML和數組互相轉換
- php解析xml字符串
- php 生成vcf通訊錄
- 文件操作相關
- php獲取文件后綴的9種方法
- PHP判斷遠程文件是否存在
- PHP獲取文件修改時間,訪問時間,inode修改時間
- php獲取遠程文件大小教程
- php 讀取文件并以文件方式下載
- php 把數字轉化為大寫中文
- 請求響應
- PHP 獲取當前訪問的URL
- 壓縮
- php生成zip壓縮包
- PHPMailer
- 整理PHPMailer 發送郵件 郵件內容為html 可以添加多個附件等
- 通達oa
- OA管理員密碼忘了怎么辦,這里教你分分鐘搞定…
- 跨域
- php解決多站點跨域
- php設置samesite cookie,有效防止CSRF
- Chrome 配置samesite=none方式
- Cookie 的 SameSite 屬性
- 圖片
- php pdf首頁截圖,PHP_PHP中使用Imagick讀取pdf并生成png縮略圖實例,pdf生成png首頁縮略圖
- PHP -- 七牛云 在線視頻 獲取某一幀作為封面圖
- PHP圖片壓縮方法
- 如何解決PHP curl或file_get_contents下載圖片損壞或無法打開的問題
- php遠程下載文章中圖片并保存源文件名不變
- 詳解PHP如何下載采集圖片到本地(附代碼實例)
- php如何將webp格式圖片轉為jpeg
- PHP獲取遠程圖片的寬高和體積大小
- php 軟件版本號比較
- 使用PHP通過SMTP發送電郵
- 常用正則表達式
- php如何用正則表達式匹配中文
- 用于分割字符串的 PHP preg_match_all 正則表達式
- 性能優化
- php.ini配置調優
- PHP 幾種常見超時的設置方法
- PHP函數in_array、array_key_exists和isset效率分析
- php array push 和array_merge 效率誰高,php 通過array_merge()和array+array合并數組的區別和效率比較...
- php 兩個數組取交集、并集、差集
- 設置PHP最大連接數及php-fpm 高并發 參數調整
- 小工具
- php 獲取代碼執行時間和消耗的內存
- PHP如何判斷某項擴展是否開啟
- centos7.x下php 導出擴展 XLSXWriter 安裝
- php生成mysql數據庫字典
- PHP 實現 word/excel/ppt 轉換為 PDF
- composer的使用
- showdoc sqlite3 找回管理員密碼
- php怎么將數組轉為xml
- PHP抖音最新視頻提取代碼
- yii
- Yii2 如何獲取Header參數?
- swoole
- Linux下搭建swoole服務的基本步驟
- 相關學習資料
- 帶你學習swoole_process詳解
- 按照官方文檔 在win10下安裝 docker for windows easyswoole鏡像 掛載目錄
- php常用框架
- Hyperf
- 常用算法PHP版
- thinkphp6
- TP6 事件綁定、監聽、訂閱
- Thinkphp 模板中輸出HTML的變量
- Thinkphp6(操作SQL數據庫)
- thinkphp6 mysql查詢語句對于為null和為空字符串給出特定值處理
- Thinkphp 6 - 連接配置多個數據庫并實現自由切換(詳細過程及實例demo)
- TP框架中的Db::name 和 dB::table 以及 db('') 的區別
- thinkphp6.0模型篇之模型的軟刪除
- thinkphp6自定義日志驅動,增加顯示全部請求信息
- 其他系統
- 微擎數據庫字段字典
- Flutter實現微信支付和iOS IAP支付
- Flutter上線項目實戰——蘋果內購
- PHP接入蘋果支付
- 調試
- php如何獲取當前腳本所有加載的文件
- php跟蹤所有調用方法,日志方法
- 解析phpstorm + xdebug 遠程斷點調試
- PHP XDEBUG調試 PHPSTORM配置
- 異常處理
- PHP 出現 502 解決方案
- php 語法解析錯誤 syntax error unexpected namespace T_NAMESPACE
- Composer 安裝與使用
- 數據庫相關
- php pdo怎么設置utf8
- php 如何根據最新聊天對用戶進行排序
- php lic&fpm
- 讓php程序在linux后臺執行
- PHPcli模式和fpm模式優缺點在哪里?
- 運行模式
- php運行模式之cli模式
- 自己庫
- php批量獲取所有公眾號粉絲openid
- 地圖
- php 判斷點在多邊形內,php百度地圖api判斷地址是否在多邊形區域內
- PHP,Mysql-根據一個給定經緯度的點,進行附近地點查詢
- MySQL 根據經緯度查找排序
- PHP+MySQL獲取坐標范圍內的數據
- 【百度地圖】刪除指定覆蓋物
- 百度地圖多點+畫連接線+數字標注
- laravel5.8
- laravel5.8(四)引入自定義常量文件及公共函數文件
- Lumen 查詢執行SQL
- 使你的 Laravel 項目模塊化
- Laravel 多條件 AND , OR條件組合查詢
- Laravel 查詢 多個or或者and條件
- laravel redis操作大全
- laravel中外部定義whereIn的用法和where中使用in
- lumen5.8
- 創建laravel5.8 lumen前后臺api項目--記錄請求和響應日志
- Laravel和Lumen開啟SQL日志記錄
- Laravel 5.8 常用操作(路徑+日志+分頁+其他操作)
- 升級php7.4 laravel lumen報錯Trying to access array offset on value of type null
- Laravel 任務調度(計劃任務,定時任務)
- laravel的command定時任務時間的設置
- Laravel任務調度的簡單使用
- laravel單數據庫執行事務和多數據庫執行事務
- laravel中鎖以及事務的簡單使用
- 申請其他相關
- 小程序地理位置接口申請
- PHP高并發
- php 高并發下 秒殺處理思路
- 記錄 PHP高并發 商品秒殺 問題的 Redis解決方案
- thinkphp3.2
- thinkphp3.2 數據庫 AND OR連綴使用
- laravel
- laravel的聯表查詢with方法的使用
- laravel獲取請求路由對應的控制器和方法
- Laravel 模型關聯建立與查詢
- Laravel多表(3張表以上)with[]關聯查詢,對關聯的模型做條件查詢(has,跟join一樣結果 )
- Laravel模型屬性的隱藏屬性、顯示屬性和臨時暴露隱藏屬性用法介紹
- aravel獲取當前的url以及當前的基礎域名方法匯總
- Laravel 模型實現多庫查詢或者多表映射
- 關于 Laravel 的 with 多表查詢問題
- Laravel 模型過濾(Filter)設計
- 懶加載、預加載、with()、load() 傻傻分不清楚?
- laravel模型$castsl屬性
- Laravel Query Builder 復雜查詢案例:子查詢實現分區查詢 partition by
- Laravel 模型關聯、關聯查詢、預加載使用實例
- laravel 中with關聯查詢限定查詢字段
- laravel 原生字段查詢 whereRaw 和 where(DB::raw(''))
- lavarel - where條件分組查詢(orWhere)
- 通過 Laravel 查詢構建器實現復雜的查詢語句
- 兩個結果集合并成一個
- Laravel 對某一列進行篩選然后求和 sum()
- laravel怎么優雅的拼接where,處理whereIn與where數組查詢的問題
- laravel查詢時判斷是否存在數據
- laravel中的whereNull和whereNotNull
- laravel框架中的子查詢
- Laravel框架中 orwhere 多條件查詢的使用
- Laravel中where的高級使用方法
- laravel復雜的數據庫查詢(事例)
- laravel多條件查詢方法(and,or嵌套查詢)
- Laravel 的 where or 查詢
- Laravel 進行where 多個or和and的條件查詢可用
- laravel Middleware 中間件 $next($request) 報錯不執行問題
- 基于Laravel框架--自定義CORS跨域中間件
- laravel9新增路由文件及解決跨域問題方法
- 解決在laravel中leftjoin帶條件查詢沒有返回右表為NULL的問題
- 【Laravel 】faker數據填充詳解
- 數據庫
- mysql
- mysql聯合索引(復合索引)詳解
- MYSQL 清空表和截斷表
- MySQL快速生成大量測試數據(100萬、1000萬、1億)
- 提高mysql千萬級大數據SQL查詢優化30條經驗(Mysql索引優化注意)
- MySQL常用命令
- MySQL(三)|《千萬級大數據查詢優化》第一篇:創建高性能的索引
- MySQL(一)|性能分析方法、SQL性能優化和MySQL內部配置優化
- MySQL(二)|深入理解MySQL的四種隔離級別及加鎖實現原理
- MySQL(四)|《千萬級大數據查詢優化》第一篇:創建高性能的索引(補充)
- MySQL(五)|《千萬級大數據查詢優化》第二篇:查詢性能優化(1)
- MySQL(六)|《千萬級大數據查詢優化》第二篇:查詢性能優化(2)
- MySQL(七)|MySQL分庫分表的那點事
- Mysql索引優化 Mysql通過索引提升查詢效率(第二棒)
- MySQL查詢的性能優化(查詢緩存、排序跟索引)
- 【總結】MySQL數據庫
- MySQL存儲引擎、事務日志并發訪問以及隔離級別
- 技巧
- 數據庫 SQL查詢重復記錄 方法
- 替換數據庫中某個字段中的部分字符
- mysql開啟bin log 并查看bin log日志(linux)
- 分表分區
- 千萬級別數據的mysql數據表優化
- MYSQL百萬級數據,如何優化
- MySQL備份和恢復
- MySQL間隙鎖死鎖問題
- 小技巧
- 基礎
- MySQL中sql_mode參數
- mysql數據庫異常
- this is incompatible with sql_mode=only_full_group_by
- mysql安全
- MySQL數據庫被比特幣勒索及安全調整
- mysql忘記密碼后重置(以windows系統mysql 8.4為例)
- MongoDB
- sql查詢
- MYSQL按時間段分組查詢當天,每小時,15分鐘數據分組
- 高級
- 基于 MySQL + Tablestore 分層存儲架構的大規模訂單系統實踐-架構篇
- 數據庫安全
- 服務器被黑,MySQL 數據庫遭比特幣勒索!該如何恢復?
- 數千臺MySQL數據庫遭黑客比特幣勒索,該怎么破?
- MySQL 數據庫規范
- MySQL數據庫開發的36條鐵律
- Elasticsearch
- 安裝與配置
- ElasticSearch關閉重啟命令
- 設置ES默認分詞器IK analyzer
- 查詢
- elasticsearch 模糊查詢不分詞,實現 mysql like
- elasticSearch多條件高級檢索語句,包含多個must、must_not、should嵌套示例,并考慮nested對象的特殊檢索
- elasticSearch按字段普通檢索,結果高亮
- Elasticsearch 如何實現查詢/聚合不區分大小寫?
- 索引更新&刷新
- refresh與批量操作的效率
- Elasticsearch 刪除type
- 分詞器
- ElasticSearch最全分詞器比較及使用方法
- 異常錯誤
- 解決ES因內存不足而無法查詢的錯誤,Data too large, data for [<http_request>]
- linux
- 基本知識
- CentOS7.5 通過wget下載文件到指定目錄
- 【CentOS】vi命令
- centos7查看硬盤使用情況
- CentOS7 查看目錄大小
- Centos 7下查看當前目錄大小及文件個數
- 普通用戶sudo\su 到root免密碼
- 普通用戶切換到root用戶下的免密配置方法
- linux 獲取進程啟動參數,linux查看進程啟動及運行時間
- Linux 查看進程
- linux刪除文件后不釋放磁盤的問題
- Linux查找大文件命令
- linux 如何關閉正在執行的php腳本
- linux三劍客(grep、sed、awk)基本使用
- centos 卸載軟件
- centos查看內存、cpu占用、占用前10,前X
- Centos 查看系統狀態
- 異常
- 問題解決:Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist:...
- php相關
- centos 安裝phpize
- Centos7.2下phpize安裝php擴展
- 切換版本
- 運營工具
- 資深Linux運維工程師常用的10款軟件/工具介紹
- 一款良心的終端連接工具
- 六款Linux常用遠程連接工具介紹,看看哪一款最適合你
- Finalshell
- Linux Finalshell連接centos7和文件無顯示問題
- WSL2:我在原生的Win10玩轉Linux系統
- MobaXterm
- 運維
- linux服務器上定時自動備份數據庫,并保留最新5天的數據
- Centos系統開啟及關閉端口
- CentOS7開放和關閉端口命令
- Linux中查看所有正在運行的進程
- 防火墻firewall-cmd命令詳解
- centos 7.8阿里云服務器掛載 數據盤
- Linux Finalshell連接centos7和文件無顯示問題
- Centos7系統端口被占用問題的解決方法
- vi
- 如何在Vim/Vi中復制,剪切和粘貼
- 命令
- [Linux kill進程] kill 進程pid的使用詳解
- 備份還原
- Linux的幾種備份、恢復系統方式
- Linux系統全盤備份方法
- 相關軟件安裝
- linux下 lua安裝
- python
- 升級pip之后出現sys.stderr.write(f“ERROR: {exc}“)
- lua
- centos源碼部署lua-5.3
- deepin
- deepin20.6設置默認的root密碼
- 任務相關
- 寶塔定時任務按秒執行
- CentOS 7 定時任務 crontab 入門
- centos7定時任務crontab
- Linux下定時任務的查看及取消
- Linux(CentOS7)定時執行任務Crond詳細說明
- Linux 查看所有定時任務
- linux查看所有用戶定時任務
- Linux 定時任務(超詳細)
- 防火墻
- Centos7開啟防火墻及特定端口
- CentOS防火墻操作:開啟端口、開啟、關閉、配置
- 生成 SSH 密鑰(windows+liunx)
- 阿里云,掛載云盤
- 前端
- layui
- layui多文件上傳
- layer.msg()彈框,彈框后繼續運行
- radio取值
- layui-數據表格排序
- Layui select選擇框添加搜索選項功能
- 保持原來樣式
- layui表格單元如何自動換行
- layui-laydate時間日歷控件使用方法詳解
- layui定時刷新數據表格
- layer 延時設置
- layer.open 回調函數
- 【Layui內置方法】layer.msg延時關閉msg對話框(代碼案例)
- layui多圖上傳圖片順序錯亂及重復上傳解決
- layer.confirm關閉彈窗
- vue
- Vue跨域解決方法
- vue 4.xx.xx版本降級至2.9.6
- vue-cli 2.x升級到3.x版本, 和3.x降級到2.x版本命令
- 最新版Vue或者指定版本
- Vue2.6.11按需模塊安裝配置
- jQuery
- jQuery在頁面加載時動態修改圖片尺寸的方法
- jquery操作select(取值,設置選中)
- 日歷
- FullCalendar中文文檔:Event日程事件
- js
- JS 之 重定向
- javascript截取video視頻第一幀作為封面方案
- HTML <video> preload 屬性
- jQuery使用ajax提交post數據
- JS截取視頻靚麗的幀作為封面
- H5案例分享:移動端touch事件判斷滑屏手勢的方向
- JS快速獲取圖片寬高的方法
- win
- Windows環境下curl的使用
- Cygwin
- Windows下安裝Cygwin及apt-cyg
- Cygwin 安裝、CMake 安裝
- mklink命令 詳細使用
- Nginx
- Nginx高級篇-性能優化
- Nginx常用命令(Linux)
- linux+docker+nginx如何配置環境并配置域名訪問
- Nginx的啟動(start),停止(stop)命令
- linux 查看nginx 安裝路徑
- 安裝配置
- Linux 查看 nginx 安裝目錄和配置文件路徑
- 【NGINX入門】3.Nginx的緩存服務器proxy_cache配置
- thinkphp6.0 偽靜態失效404(win下)
- 深入
- nginx rewrite及多upstream
- Nginx負載均衡(upstream)
- 專業術語
- 耦合?依賴?耦合和依賴的關系?耦合就是依賴
- PHP常用六大設計模式
- 高可用
- 分布式與集群
- Nginx 實踐案例:反向代理單臺web;反向代理多組web并實現負載均衡
- 容器
- Docker
- 30 分鐘快速入門 Docker 教程
- linux查看正在運行的容器,說說Docker 容器常用命令
- Windows 安裝Docker至D盤
- 配置
- win10 快速搭建 lnmp+swoole 環境 ,部署laravel6 與 swoole框架laravel-s項目1
- win10 快速搭建 lnmp+swoole 環境 ,部署laravel6 與 swoole框架laravel-s項目2
- docker 容器重命名
- Linux docker常用命令
- 使用
- docker 搭建php 開發環境 添加擴展redis、swoole、xdebug
- docker 單機部署redis集群
- Docker 退出容器不停止容器運行 并重新進入正在運行的容器
- 進入退出docker容器
- Docker的容器設置隨Docker的啟動而啟動
- 使用異常處理
- docker容器中bash: vi: command not found
- OCI runtime exec failed: exec failed:解決方法
- docker啟動容器慢,很慢,特別慢的坑
- 解決windows docker開發thinkphp6啟動慢的問題
- 【Windows Docker】docker掛載解決IO速度慢的問題
- Docker的網絡配置,導致Docker使用網路很慢的問題及解決辦法
- golang工程部署到docker容器
- Docker 容器設置自啟動
- 如何優雅地刪除Docker鏡像和容器(超詳細)
- 5 個好用的 Docker 圖形化管理工具
- Docker 可能會用到的命令
- Kubernetes
- 消息隊列
- RabbitMQ
- php7.3安裝使用rabbitMq
- Windows環境PHP如何使用RabbitMQ
- RabbitMQ學習筆記:4369、5672、15672、25672默認端口號修改
- Window10 系統 RabbitMQ的安裝和簡單使用
- RabbitMQ默認端口
- RabbitMQ可視化界面登錄不了解決方案
- RocketMQ
- Kafka
- ActiveMQ
- mqtt
- phpMQTT詳解以及處理使用過程中內存耗死問題
- MQTT--物聯網(IoT)消息推送協議
- php實現mqtt發布/發送 消息到主題
- Mqtt.js 的WSS鏈接
- emqx
- 如何在 PHP 項目中使用 MQTT
- emqx 修改dashboard 密碼
- 其他
- Windows 系統中單機最大TCP的連接數詳解
- EMQX
- Linux系統EMQX設置開機自啟
- Centos7 EMQX部署
- docker安裝 EMQX 免費版 docker安裝并配置持久化到服務器
- 實時數倉
- 網易云音樂基于 Flink + Kafka 的實時數倉建設實踐
- 實時數倉-基于Flink1.11的SQL構建實時數倉探索實踐
- 安全
- 網站如何保護用戶的密碼
- 關于web項目sessionID欺騙的問題
- php的sessionid可以偽造,不要用來做防刷新處理了
- DVWA-Weak Session IDs (弱會話)漏洞利用方式
- 保證接口數據安全的10種方案
- cookie和session的竊取
- 萬能密碼漏洞
- 黑客如何快速查找網站后臺地址方法整理
- 網站后臺萬能密碼/10大常用弱口令
- 萬能密碼漏洞02
- 大多數網站后臺管理的幾個常見的安全問題注意防范
- token可以被竊取嗎_盜取token
- token被劫持[token被劫持如何保證接口安全性]
- PHP給后臺管理系統加安全防護機制的一些方案
- php - 重新生成 session ID
- 隱藏響應中的server和X-Powered-By
- PHP會話控制之如何正確設置session_name
- Session攻擊001
- PHP防SQL注入代碼,PHP 預防CSRF、XSS、SQL注入攻擊
- php25個安全實踐
- php架構師 系統管理員必須知道的PHP安全實踐
- 版本控制
- Linux服務器關聯Git,通過執行更新腳本實現代碼同步
- PHP通過exec執行git pull
- git 在linux部署并從windows上提交代碼到linux
- git上傳到linux服務器,git一鍵部署代碼到遠程服務器(linux)
- linux更新git命令,使用Linux定時腳本更新服務器的git代碼
- git異常
- 如何解決remote: The project you were looking for could not be found
- git status顯示大量文件修改的原因是什么
- PHPstorm批量修改文件換行符CRLF為LF
- git使用
- git常用命令大全
- centos git保存賬戶密碼
- GIT 常用命令
- git怎樣還原修改
- Git 如何放棄所有本地修改的方法
- Git忽略文件模式改變
- git: 放棄所有本地修改
- Git三種方法從遠程倉庫拉取指定的某一個分支
- 雜七雜八
- h5視頻
- H5瀏覽器支持播放格式:H264 AVCA的MP4格式,不能轉換為mpeg-4格式,
- iOS無法播放MP4視頻文件的解決方案 mp4視頻iphone播放不了怎么辦
- h5點播播放mp4視頻遇到的坑,ios的h5不能播放視頻等
- 【Linux 并發請求數】支持多少并發請求數
- Linux下Apache服務器并發優化
- 緩存
- redis
- Linux啟動PHP的多進程任務與守護redis隊列
- 重啟redis命令
- golang