序
很多系統(tǒng)在登錄的時(shí)候會(huì)使用明文密碼的方式往后端進(jìn)行傳參,即使傳輸過(guò)程中使用的是HTTPS屋摔,但是如果在中間人攻擊中烁设,或者釣魚網(wǎng)站。攻擊者過(guò)于輕松的從中截取到明文的密碼钓试。
用戶登錄后的權(quán)限的Token或者Cookie會(huì)用明文的方式在HTTPS上進(jìn)行傳輸装黑,其實(shí)如果出現(xiàn)上述場(chǎng)景太容易出現(xiàn)問(wèn)題。
-
輸入密碼場(chǎng)景弓熏,在Web 前端進(jìn)行明文加密后進(jìn)行傳輸是否有意義恋谭?
- 必須要做,雖然不是決定性的保護(hù)措施硝烂,但卻是很有意義的低成本安全增強(qiáng)方案箕别。
-
在登錄后的有權(quán)限的接口上建立在HTTPS上是否需要在應(yīng)用上層實(shí)現(xiàn)加密是否有意義铜幽?
- 如果有開(kāi)發(fā)資源的情況下,建議需要做串稀。
- 在開(kāi)發(fā)資源不寬裕的團(tuán)隊(duì)中在無(wú)權(quán)限接口場(chǎng)景中除抛,不建議使用密文傳輸,但是建議加入簽名機(jī)制防止傳輸過(guò)程中篡改母截。主要原因是如果是密文傳輸到忽,中間內(nèi)容不可見(jiàn)。會(huì)直接影響到調(diào)試過(guò)程清寇,導(dǎo)致開(kāi)發(fā)成本的提升喘漏。另外如果接入一些三方系統(tǒng)時(shí),比如WAF华烟,日志翩迈,路由等,因?yàn)閮?nèi)容不可見(jiàn)盔夜,可能會(huì)導(dǎo)致早期開(kāi)發(fā)成本較高负饲。所以退一步,加入簽名機(jī)制喂链,傳輸過(guò)程中雖然明文可見(jiàn)的返十,但是可以保證數(shù)據(jù)的完整性,簽名過(guò)程中加入過(guò)期時(shí)間椭微,也可以在一些場(chǎng)景下防止請(qǐng)求被重放洞坑,在一些要求限制請(qǐng)求延遲的接口中還可以。
-
在所有接口上建立加密傳輸蝇率,或者簽名是否需要做迟杂?
- 如果開(kāi)發(fā)資源豐富的情況下,建議需要做本慕。
- 在開(kāi)發(fā)資源不寬裕的團(tuán)隊(duì)中在公開(kāi)接口場(chǎng)景中逢慌,基于HTTPS時(shí)不暫時(shí)做過(guò)多的開(kāi)發(fā)。
所有場(chǎng)景间狂,加密總會(huì)比不加密要安全,但是必須考慮的是開(kāi)發(fā)成本和收益的問(wèn)題火架。
敏感操作需要使用鉴象,2FA是一個(gè)比較有效的解決方案,一定要注意防重復(fù)的問(wèn)題何鸡。當(dāng)然如果在中間人攻擊中如果中間人篡改的源碼纺弊,那么這些防御基本都會(huì)失效,所以搭配App掃碼驗(yàn)證的的方式就有很好的效果骡男。
-
在權(quán)限接口中肯能會(huì)出現(xiàn)的風(fēng)險(xiǎn)
- 竊聽(tīng)風(fēng)險(xiǎn)(eavesdropping):第三方可以獲知通信內(nèi)容淆游。
- 篡改風(fēng)險(xiǎn)(tampering):第三方可以修改通信內(nèi)容。
- 冒充風(fēng)險(xiǎn)(pretending):第三方可以冒充他人身份參與通信。
實(shí)踐
這里提供一個(gè)解決思路犹菱,可以適用于一些場(chǎng)景拾稳,需要按照具體場(chǎng)景進(jìn)行適配。還有一個(gè)備選的方案腊脱,在 https 上做一層類似https的協(xié)議访得,https比較安全的原因很大一部分是證書有效性的驗(yàn)證依賴于操作系統(tǒng),但是如果沒(méi)有的操作系統(tǒng)的驗(yàn)證陕凹,如果單純的類似https的協(xié)議悍抑,收益不大,所以這個(gè)實(shí)踐中就融入了業(yè)務(wù)的流程杜耙。
服務(wù)端信息存儲(chǔ)的設(shè)計(jì)
- 用戶名/郵箱/手機(jī)號(hào)存儲(chǔ):關(guān)于用戶信息的保存搜骡,這里可能有些做法中會(huì)選擇使用對(duì)稱加密的方式保存,這里需要注意的是開(kāi)發(fā)成本和收益佑女,建議使用明文保存全文(應(yīng)對(duì)業(yè)務(wù)的變動(dòng))记靡。業(yè)務(wù)穩(wěn)定后可以選擇明文保存隱藏部分內(nèi)容的,(便于明文搜索)珊豹,密文保存整體內(nèi)容簸呈。
-
密碼的存儲(chǔ):必須使用單向的Hash算法,并且加鹽店茶。推薦使用Bcrypt蜕便。前端發(fā)送值為 Bcrypt( Raw ) 得到 Val_0,后端保存值為 提取Val_0的salt值為( Val_1 )贩幻,Val_1 + (Delimiter) + Hash256( Val_0 )
- 要求1:后端是不能直接接收明文的密碼的轿腺,所以前端發(fā)送的時(shí)候就必須要進(jìn)行加密
- 要求2:后端需要進(jìn)行加密保存。所以這里保存之前的salt和加密后的值丛楚,這里使用了Hash256的方式進(jìn)行加密族壳,這里也可以再次使用Bcrypt進(jìn)行加密,但是在Secret設(shè)計(jì)中需要獲取這次Bcrypt中使用的Salt值趣些。
請(qǐng)求簽名的設(shè)計(jì)
這里使用Hmac的思路進(jìn)行請(qǐng)求簽名的方式仿荆。Hash( Raw + Salt ),可以看到如果按照這個(gè)思路中坏平,前端需要保存Salt拢操,并且后端需要持有相同的Salt,這里因?yàn)?原始的明文需要傳輸所以Salt盡可能不在相同的渠道進(jìn)行傳輸舶替,或者不傳輸令境。
技術(shù)選型:
這里使用JWT組件來(lái)做簽名組件,Algorithm采用Hash256顾瞪,需要加入過(guò)期時(shí)間字段舔庶。這個(gè)組件在多個(gè)平臺(tái)都有成熟的實(shí)現(xiàn)抛蚁。組件中的 Secret 可以類比成salt,基本原理和Hmac類似惕橙。這里為了防止整個(gè)請(qǐng)求參數(shù)被篡改瞧甩,那么簽名中需要有請(qǐng)求參數(shù)的hash值,所以在這個(gè)思路中吕漂,需要有一個(gè)比較合適的通用請(qǐng)求方式亲配,所以最好是Post請(qǐng)求,并且所有的請(qǐng)求參數(shù)都放到請(qǐng)求體中惶凝,這樣會(huì)更加便利于這個(gè)思路的實(shí)現(xiàn)吼虎。
signature = JWT(payload={
"digest": hash256(RequestBody),
"otherFields": "otherFieldsValues",
"exp": expireTime
}, key=secretKey, algorithm='HS256')
SecretKey 的設(shè)計(jì)
現(xiàn)在來(lái)看可以看到,SecretKey 的安全性苍鲜,成了關(guān)鍵思灰,這里使用辦法是將用戶輸入值作為SecretKey相關(guān)的生成方式。這里可以適配兩個(gè)比較普遍的場(chǎng)景混滔,密碼登錄洒疚,驗(yàn)證碼登錄。
這里主要的思路在登錄過(guò)程中將用戶的輸入在前端計(jì)算出和注冊(cè)流程中一樣的值坯屿,然后將對(duì)稱的值當(dāng)做SecretKey使用油湖,這樣在登錄流程中SecretKey就會(huì)這個(gè)值就不會(huì)出現(xiàn)在傳輸中并且一邊密碼的強(qiáng)度又比較高,所以登錄過(guò)程相對(duì)會(huì)比較安全领跛。
注冊(cè)流程中乏德, 由上述流程中可以看出如果可以在可以通過(guò)別的渠道獲取到和后端一樣的對(duì)稱密鑰,那么整個(gè)流程會(huì)安全很多吠昭,這里提供一個(gè)方式采用驗(yàn)證的URL的方式喊括,這里需要注意的是密鑰需要是用URL hash的方式存放到URL上,用戶在注冊(cè)驗(yàn)證的時(shí)候矢棚,點(diǎn)開(kāi)URL郑什,默認(rèn)情況下瀏覽器是不會(huì)發(fā)送Url Hash # 后的部分的,只有客戶端可以獲取到蒲肋。
流程參考:
注冊(cè)流程
- 前端輸入用戶名/郵箱/手機(jī)號(hào)和密碼(驗(yàn)證碼)
- 前端收集到密碼后
- 然后再次使用Bcrypt對(duì)密碼進(jìn)行加密得到值 Val_0
- 后端如果驗(yàn)證通過(guò)蘑拯,將 Salt( Val_0 ) + (Delimiter) + Hash256( Val_0 ) 進(jìn)行持久化的存儲(chǔ),返回成功兜粘。
- 前端跳回登錄强胰,如果不登錄的場(chǎng)景,前端使用登錄相同的流程進(jìn)行登錄妹沙。(這里最好不要由后端自動(dòng)登錄權(quán)限)
登錄流程
前端輸入用戶名/郵箱/手機(jī)號(hào)和密碼/驗(yàn)證碼(需要加入人機(jī)驗(yàn)證,所有公開(kāi)的寫類型操作的接口必須要做人機(jī)驗(yàn)證熟吏,類似的發(fā)送短信距糖、郵箱驗(yàn)證玄窝,登錄,注冊(cè)悍引,等等恩脂,驗(yàn)證碼的場(chǎng)景下驗(yàn)證碼的強(qiáng)度不足,可能會(huì)被破解趣斤,可以采用字母+數(shù)字的驗(yàn)證俩块,驗(yàn)證通過(guò)后使用當(dāng)前密鑰換取強(qiáng)度較大的密鑰,但可能會(huì)被跟蹤浓领,驗(yàn)證碼和密碼登錄有同等的做法)
前端請(qǐng)求接口得到后端保存的密碼的Salt的值
-
前端收集到密碼后
- 使用Bcrypt( Raw ) 和后端取得的Salt得到值 Val_0玉凯,在進(jìn)行Val_1 + (Delimiter) + Hash256 ( Val_0 ) 得到 Val_1,這樣前端可以計(jì)算出和后端存儲(chǔ)一樣的值联贩,
- 然后 Hash256( Val_1 ) 得到 Val_2
- 然后再次使用Bcrypt對(duì)密碼使用新的Salt值進(jìn)行加密得到值 Val_3
- 使用Val_1作為Key對(duì)Val_3進(jìn)行AES加密漫仆,前端需要保存Val_1這個(gè)值,如果后端驗(yàn)證通過(guò)泪幌,那么就Val_1 當(dāng)做 Secret 來(lái)使用盲厌,
- 后端如果驗(yàn)證通過(guò),把存儲(chǔ)中的密碼Hash值作為身份的驗(yàn)證的SecretKey祸泪,然后把 Val_3 更新原有的密碼的Hash值吗浩,并返回通過(guò),進(jìn)行后續(xù)的操作没隘。(這里需要注意密鑰的有效期懂扼,需要在上一個(gè)密鑰未過(guò)期的時(shí)候來(lái)更換密鑰,更換方式使用當(dāng)前密鑰做key然后使用對(duì)稱加密的方式即可)
如有問(wèn)題歡迎留言或者私信