??大家好,我是陳哈哈,北漂五年,相信大家和我一樣,
都有一個大廠夢,作為一名資深Java選手,深知面試重要性,接下來我準備用100天時間,基于Java崗面試中的高頻面試題,以每日3題的形式,帶你過一遍熱門面試題及恰如其分的解答,
??一路走來,隨著問題加深,發現不會的也愈來愈多,但底氣著實足了不少,相信不少朋友和我一樣,榷訓月累才是最有效的學習方式!想起高三時一個同學的座右銘:只有沉下去,才能浮上來,共勉(juan),

群里同學的搬磚工地近景:上海,不得不說,上海才令我深感都市氣息,
作者:這樣Ba
車票
- 面試題1:你們是怎樣保存用戶密碼等敏感資料的?
- 面試題2:怎么控制用戶請求的冪等性的?
- 面試題3:你們是如何預防SQL注入問題的?
- 每日小結
??本欄目Java開發崗高頻面試題主要出自以下各技術堆疊:Java基礎知識、集合容器、并發編程、JVM、Spring全家桶、MyBatis等ORMapping框架、MySQL資料庫、Redis快取、RabbitMQ訊息佇列、Linux操作技巧等,
面試題1:你們是怎樣保存用戶密碼等敏感資料的?
??本題回答參考朱曄的《Java業務開發常見錯誤100例》

??我們知道,用戶名、密碼、身份證等都屬于用戶敏感資訊,其中最敏感的資料恐怕就是用戶的密碼了,黑客一旦竊取了用戶密碼,就可以登錄進用戶的賬號,消耗其資產、發布不良資訊等;更可怕的是,有些用戶至始至終都是使用一套密碼,密碼一旦泄露,就可以被黑客通過撞庫來登錄全網各大平臺,嘿嘿嘿,
??為了防止密碼泄露,最重要的原則是不要在資料庫保存用戶原始密碼,
??大家經常說,不要明文保存用戶密碼,應該把密碼通過 MD5 加密后保存,這的確是一個正確的方向,但這個說法并不準確,
??首先,MD5 其實不是真正的加密演算法,所謂加密演算法,是可以使用密鑰把明文加密為密文,隨后還可以使用密鑰解密出明文,是雙向的,而 MD5 是散列、哈希演算法或者摘要演算法,不管多長的資料,使用 MD5 運算后得到的都是固定長度的摘要資訊或指紋資訊,無法再解密為原始資料,所以,MD5 是單向的,最重要的是,僅僅使用 MD5 對密碼進行摘要,并不安全,
??比如,使用如下代碼在保持用戶資訊時,對密碼進行了 MD5 計算:
UserData userData = new UserData();
userData.setId(1L);
userData.setName(name);
//密碼欄位使用MD5哈希后保存
userData.setPassword(DigestUtils.md5Hex(password));
return userRepository.save(userData);
通過輸出,可以看到密碼是 32 位的 MD5:
"password": "325a2cc052914ceeb8c19016c091d2ac"
??然后,再去破解網站試一下這個 MD5,就可以得到原始密碼是 salt,也就知道了鹽值是 salt:

??其實,知道鹽是什么沒什么關系,關鍵的是我們是在代碼里寫死了鹽,并且鹽很短、所有用戶都是這個鹽,這么做就會有三個問題:
- 因為鹽太短、太簡單了,如果用戶原始密碼也很簡單,那么整個拼起來的密碼也很短,這樣一般的
MD5 破解網站都可以直接解密這個 MD5,除去鹽就是原始密碼了, - 相同的鹽,意味著使用相同密碼的用戶 MD5 值是一樣的,
知道了一個用戶的密碼就可能知道了多個, - 黑客也可以使用這個鹽來構建一張
彩虹表,也就是字典表,雖然會花不少代價,但是一旦構建完成,所有人的密碼都可以被破解,
??所以,最好是每一個密碼都有獨立的鹽,并且鹽要長一點,比如超過 20 位,
??第二,雖然說每個人的鹽最好不同,但也不建議將一部分用戶資料作為鹽,比如,使用用戶名作為鹽:
userData.setPassword(DigestUtils.md5Hex(name + password));
??如果世界上所有的系統都是按照這個方案來保存密碼,那么 root、admin 這樣的用戶使用再復雜的密碼也總有一天會被破解,因為黑客們完全可以針對這些常用用戶名來做彩虹表,所以,鹽最好是隨機的值,并且是全球唯一的,意味著全球不可能有現成的彩虹表給你用,
??正確的做法是,使用全球唯一的、和用戶無關的、足夠長的隨機值作為鹽,比如,可以使用 UUID 作為鹽,把鹽一起保存到資料庫中:
userData.setSalt(UUID.randomUUID().toString());
userData.setPassword(DigestUtils.md5Hex(userData.getSalt() + password));
??并且每次用戶修改密碼的時候都重新計算鹽,重新保存新的密碼,
??需要注意的是,這么做雖然黑客已經很難通過彩虹表來破解密碼了,但是仍然有可能暴力破解密碼,也就是對于同一個用戶名使用常見的密碼逐一嘗試登錄,因此,除了做好密碼哈希保存的作業外,我們還要建設一套完善的安全防御機制,在感知到暴力破解危害的時候,開啟短信驗證、圖形驗證碼、賬號暫時鎖定等防御機制來抵御暴力破解,
那么姓名和身份證又是怎么保存的?
??我們把姓名和身份證,叫做二要素,
??現在互聯網非常發達,很多服務都可以在網上辦理,很多網站僅僅依靠二要素來確認你是誰,所以,二要素是比較敏感的資料,如果在資料庫中明文保存,那么資料庫被攻破后,黑客就可能拿到大量的二要素資訊,如果這些二要素被用來申請貸款等,后果不堪設想,
??之前我們提到的單向散列演算法(MD5),顯然不適合用來加密保存二要素,因為資料無法解密,這個時候,我們需要選擇真正的加密演算法,可供選擇的演算法,包括對稱加密和非對稱加密演算法兩類,
對稱加密演算法:是使用相同的密鑰進行加密和解密,使用對稱加密演算法來加密雙方的通信的話,雙方需要先約定一個密鑰,加密方才能加密,接收方才能解密,如果密鑰在發送的時候被竊取,那么加密就是白忙一場,因此,這種加密方式的特點是,加密速度比較快,但是密鑰傳輸分發有泄露風險,

非對稱加密演算法:或者叫公鑰密碼演算法,公鑰密碼是由一對密鑰對構成的,使用公鑰或者說加密密鑰來加密,使用私鑰或者說解密密鑰來解密,公鑰可以任意公開,私鑰不能公開,使用非對稱加密的話,通信雙方可以僅分享公鑰用于加密,加密后的資料沒有私鑰無法解密,因此,這種加密方式的特點是,加密速度比較慢,但是解決了密鑰的配送分發安全問題,

??但是,對于保存敏感資訊的場景來說,加密和解密都是我們的服務端程式,不太需要考慮密鑰的分發安全性,也就是說使用非對稱加密演算法沒有太大的意義,我們一般會使用對稱加密演算法來加密資料,常見的對稱加密演算法有:DES、3DES 和 AES,


課間休息,欣賞一下來自咱們群里同學的搬磚工地附近,坐標:成都 太古里,
作者:唐伯虎點香煙
面試題2:怎么控制用戶請求的冪等性的?
冪等性:對于同一筆業務操作,不管呼叫多少次,得到的結果都是一樣的,
當然,有些操作是天然冪等的,如:
-
查詢操作:查詢一次和查詢多次,在資料不變的情況下,查詢結果是一樣的,select是天然的冪等操作;
-
洗掉操作:洗掉操作也是冪等的,洗掉一次和多次洗掉都是把資料洗掉,
后臺控制冪等性的幾種途徑:
設定唯一索引:防止新增臟資料
??比如支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎么防止給用戶創建資金賬戶多個,那么給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄,要點:唯一索引或唯一組合索引來防止新增資料存在臟資料(當表存在唯一索引,并發時新增報錯時,再查詢一次就可以了,資料應該已經存在了,回傳結果即可);
token機制:防止頁面重復提交
??原理上通過session token來實作的(也可以通過redis來實作),當客戶端請求頁面時,服務器會生成一個亂數Token,并且將Token放置到session當中,然后將Token發給客戶端(一般通過構造hidden表單),下次客戶端提交請求時,Token會隨著表單一起提交到服務器端,
??服務器端第一次驗證相同過后,會將session中的Token值更新下,若用戶重復提交,第二次的驗證判斷將失敗,因為用戶提交的表單中的Token沒變,但服務器端session中Token已經改變了,
悲觀鎖
獲取資料的時候加鎖獲取,
select * from table_xxx where id='xxx' for update;
注意:id欄位一定是主鍵或者唯一索引,不然是鎖表,會死人的;悲觀鎖使用時一般伴隨事務一起使用,資料鎖定時間可能會很長,根據實際情況選用;
樂觀鎖
??樂觀鎖只是在更新資料那一刻鎖表,其他時間不鎖表,所以相對于悲觀鎖,效率更高,樂觀鎖的實作方式多種多樣,可以通過version或者其他狀態條件判斷:
- 通過版本號實作
update table_xxx set name=#name#,version=version+1 where version=#version#;
- 通過條件限制
update table_xxx set avai_amount=avai_amount where avai_amount >= 0
分布式鎖
??如果是分布式系統,構建全域唯一索引比較困難,例如唯一性的欄位沒法確定,這時候可以引入分布式鎖,通過第三方的系統(redis或zookeeper),在業務系統插入資料或者更新資料,獲取分布式鎖,然后做操作,之后釋放鎖,這樣其實是把多執行緒并發的鎖的思路,引入多多個系統,也就是分布式系統中得解決思路,
??要點:某個長流程處理程序要求不能并發執行,可以在流程執行之前根據某個標志(用戶ID+后綴等)獲取分布式鎖,其他流程執行時獲取鎖就會失敗,也就是同一時間該流程只能有一個能執行成功,執行完成后,釋放分布式鎖(分布式鎖要第三方系統提供);

北京 西二旗早高峰
面試題3:你們是如何預防SQL注入問題的?
SQL注入攻擊的總體思路
- 尋找到SQL注入的位置
- 判斷服務器型別和后臺資料庫型別
- 針對不通的服務器和資料庫特點進行SQL注入攻擊
預防方式:
1、PreparedStatement(簡單有效)
??采用預編譯陳述句集,它內置了處理SQL注入的能力,只要使用它的setXXX方法傳值即可,
??sql注入只對sql陳述句的準備(編譯)程序有破壞作用,而PreparedStatement在執行階段只是把輸入串作為資料處理,不再對sql陳述句進行決議,因此也就避免了sql注入問題,
2、使用正則運算式過濾傳入的引數
要引入的包:
import java.util.regex.*;
正則運算式:
private String CHECKSQL = “^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$”;
判斷是否匹配:
Pattern.matches(CHECKSQL,targerStr);
下面是常用來過濾引數是否存在SQL注入的正則運算式:
- 檢測SQL meta-characters的正則運算式 :
/(\%27)|(\')|(\-\-)|(\%23)|(#)/ix - 修正檢測SQL meta-characters的正則運算式 :
/((\%3D)|(=))[^\n]*((\%27)|(\')|(\-\-)|(\%3B)|(:))/i - 典型的SQL 注入攻擊的正則運算式 :
/\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix - 檢測SQL注入,UNION查詢關鍵字的正則運算式 :
/((\%27)|(\'))union/ix(\%27)|(\')
3.使用正則運算式過濾傳入的URL
??比較通用的一個方法,jsp中呼叫該函式檢查是否包函非法字符,防止SQL從URL注入:
(||之間的引數可以根據自己程式的需要添加)
public static boolean sql_inj(String str){
String inj_str = "'|and|exec|insert|select|delete|update|
count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
String inj_stra[] = split(inj_str,"|");
for (int i=0 ; i < inj_stra.length ; i++ ){
if (str.indexOf(inj_stra[i])>=0){
return true;
}
}
return false;
}
每日小結
??今天我們復習了面試中常問到的網路安全方面的三個問題,你做到心中有數了么?對了,如果你的朋友也在準備面試,請將這個系列扔給他,如果他認真對待,肯定會感謝你的!!好了,今天就到這里,學廢了的同學,記得在評論區留言:打卡,,給同學們以激勵,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/295167.html
標籤:java
