1串远、用戶登錄認(rèn)證時(shí)裹匙,如用戶不存在舒帮,仍然檢查密碼会喝,以防止黑客嗅探用戶是否存在(SEC-2056)。
相關(guān)源碼:
org.springframework.security.authentication.dao.DaoAuthenticationProvider#retrieveUser
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
presentedPassword, null);
}
throw notFound;
}
}
2玩郊、比較密碼是否匹配時(shí)肢执,采用時(shí)間恒等的算法
如果服務(wù)端密碼是使用哈希值進(jìn)行存儲(chǔ)的,在登錄時(shí)使用固定的時(shí)間來比較哈希值可以防止黑客對(duì)系統(tǒng)使用基于時(shí)間差的攻擊译红,以此獲取密碼的哈希值预茄,然后進(jìn)行本地破解。
在Java中侦厚,通常我們比較兩個(gè)字節(jié)序列(字符串)是否相同的做法是使用String的equals()方法耻陕,即從第一個(gè)字節(jié)開始,每個(gè)字節(jié)逐一順序比較刨沦。只要發(fā)現(xiàn)某個(gè)字節(jié)不同诗宣,就可以知道它們是不同的,立即返回false想诅。如果遍歷整個(gè)字符串沒有找到不同的字節(jié)召庞,可以確認(rèn)兩個(gè)字符串就是相同的岛心,可以返回true。這意味著比較兩個(gè)字符串篮灼,如果它們相同的長度不一樣鹉梨,花費(fèi)的時(shí)間不一樣。開始部分相同的長度越長穿稳,花費(fèi)的時(shí)間也就越長存皂。
例如,字符串 “ABCD” 和 “123456” 的equals比較逢艘,會(huì)立即看到旦袋,第一個(gè)字符是不同的,就不需要檢查字符串的其余部分它改。相反疤孕,當(dāng)字符串 “11111111111a” 和 “11111111111b” 進(jìn)行比較時(shí),比較算法就需要遍歷最后一位前所有的 “1” 央拖,然后才能知道它們是不同的祭阀。
假設(shè)黑客試圖入侵一個(gè)在線系統(tǒng),這個(gè)系統(tǒng)限制了每秒只能嘗試一次用戶認(rèn)證鲜戒,還假設(shè)攻擊者已經(jīng)知道系統(tǒng)對(duì)密碼進(jìn)行哈希的所有參數(shù)(salt专控、哈希函數(shù)等),但是不知道密碼存儲(chǔ)在數(shù)據(jù)庫的哈希值和密碼明文遏餐。如果黑客能精確測(cè)量在線系統(tǒng)比較猜測(cè)的密碼和真實(shí)密碼的實(shí)際耗時(shí)伦腐,那么他就能使用時(shí)序攻擊獲得密碼的哈希值,然后再進(jìn)行離線破解失都,從而繞過系統(tǒng)對(duì)認(rèn)證頻率的限制柏蘑。
首先黑客準(zhǔn)備256個(gè)字符串,它們的哈希值的第一字節(jié)包含了所有可能的情況粹庞,然后將每個(gè)字符串發(fā)送給在線系統(tǒng)嘗試登陸咳焚,并記錄系統(tǒng)響應(yīng)所消耗的時(shí)間。耗時(shí)最長的字符串就是第一字節(jié)相匹配的庞溜。攻擊者知道第一字節(jié)后革半,并可以用同樣的方式繼續(xù)猜測(cè)第二字節(jié)、第三字節(jié)等等强缘。一旦黑客獲得足夠長的哈希值片段督惰,他就可以在自己的機(jī)器上來破解不傅,不受在線系統(tǒng)的限制旅掂。
如果你用了spring cloud 自帶的BCryptPasswordEncoder,相關(guān)源碼如下访娶,如果你實(shí)現(xiàn)自定義的密碼驗(yàn)證商虐,也請(qǐng)保證使用時(shí)間恒等的安全算法。
相關(guān)源碼:
org.springframework.security.crypto.bcrypt.BCrypt#equalsNoEarlyReturn
static boolean equalsNoEarlyReturn(String a, String b) {
char[] caa = a.toCharArray();
char[] cab = b.toCharArray();
if (caa.length != cab.length) {
return false;
}
byte ret = 0;
for (int i = 0; i < caa.length; i++) {
ret |= caa[i] ^ cab[i];
}
return ret == 0;
}