1. 程式人生 > 實用技巧 >java 開發必備的安全架構知識

java 開發必備的安全架構知識

1.XXS攻擊

原因:

XXS攻擊是js指令碼攻擊,將網站上傳給後臺的引數設定成js指令碼格式:<script>...<script/>格式,當另外一個頁面需要展示這個引數時就會載入此引數,從而造成<body>${name}<body/>變成<body><script>...<scritp/><body/>形式,執行指令碼檔案。

黑客會利用此漏洞進行頁面非法跳轉等,如釣魚網站,或嘗試用js讀取本地cookie檔案資訊後傳送給自己。

解決:

java後臺建立自定義過濾器,將請求中的所有<>這樣的特殊欄位全部轉義成

%lt格式。

2.sql注入

原因:

後臺sql拼接時沒有用預編譯格式,myabits中表現形式為${name},底層jdbc表現形式為statement拼接。如:where password = '' or 1=1。

另外,在一些專案中會有人使用java拼接sql語句,也會導致sql注入問題,因為sql在java中拼接不會進行轉義。

解決:

mybatis中使用#{name}形式拼接,底層jdbc會使用preparestamtement預編譯方式拼接sql。而preparestamtement的本質是將所有特殊符號比如=!()/等字元全部轉義。

3.防盜鏈

原因:

其他網站、域名可以直接將本網站域名下的資源(可以是視訊、圖片等檔案)引用,造成請求複用,沒有訪問本網站資源卻在其他網站傳送靜態請求獲取資源。

解決:

通過自定義filter,在filter處判斷reference 欄位,根據欄位名稱去資料庫查詢相應的域名,如果等於資料庫中的白名單就放行,如果不等於就返回一張錯誤圖片回去。可以放到閘道器處對靜態資源進行優先判斷

4.網路延遲引起的介面冪等性

原因:

因為網路延遲或者使用者多次點選等原因,前臺向後臺傳送多個請求,在表單提交處可能會引起多次提交造成資料重複。

解決:

不推薦悲觀鎖實現,建議使用臨時token(實際最常用辦法),在進入頁面時,或者在傳送請求前,就在後臺生成一個唯一token存進sessionredis、或一個IOC容器(CurrentHashMap)中,將生成的token

返回給前臺。

前臺傳送請請求時帶上token資訊,前臺一般可以存在隱藏input中或者,先從容器中取出並移除token,並重新生成一個唯一token存進容器,返回給前臺。此辦法可以解決多次提交性問題。

臨時token推薦使用註解形式實現。

5.機器模擬請求攻擊

原因:

黑客熟悉伺服器臨時token機制,通過程式碼不斷獲取臨時token並且不斷訪問其他介面,造成伺服器忙。

解決:

在請求傳送前提示輸入驗證碼,輸入密碼口令等。

6. 身份偽造請求問題(包含CSRF跨域攻擊)

原因:

有兩種形式。

第一種為偽造網站,讓使用者在偽造網站處呼叫目標伺服器的介面,通過驗證並執行程

序。

第二種為黑客通過抓包、XXS攻擊等方式拿到永續性token(JWT、cookie等形式存到前臺),

然後通過該token偽造身份去訪問請求,伺服器接收token後解析,執行程式。

解決:

第一種情況,因為不是伺服器域名下可以採用禁止跨域,新增臨時token,判斷refere欄位是否是本伺服器白名單來解決。

第二種情況,因為是本伺服器域名下,可以採用傳送驗證碼,輸入密碼,驗證身份等操作來保證是本人的操作。

7. 忘記密碼漏洞

原因:

點選忘記密碼,流程一般是傳送驗證碼,然後填上幾位數字驗證碼後設置新密碼,如果這個步驟後臺沒有對介面進行限制,可以通過暴力破解方法,多執行緒不斷改變數字去訪問介面,如果驗證碼位數過低很有可能短時間內就會破解出來。

解決:

對修改密碼介面進行限流和計數,超過5次就開啟驗證碼功能,或者在閘道器API對短時間訪問過多的異常IP直接封禁(黑名單系統)。

8. 上傳檔案漏洞

原因:

通常在一些jsp專案中,上傳的靜態資源沒有存放到靜態資源伺服器中,而是存到了專案中,如果不對檔案型別進行限制,黑客可以上傳jsp檔案並且在jsp檔案中寫上相應的java程式碼,直接呼叫該檔案地址時就會執行內部的java程式碼,可能會危害整個伺服器上的檔案。

解決:

只對檔案字尾進行判斷無法阻止黑客上傳可執行檔案,正規做法是通過檔案流的拼接,根據檔案流欄位上的二進位制資料轉成字元,根據字元判斷檔案型別。

9. 前端漏洞

原因:

部分系統講使用者的ID,手機號等資訊儲存在前端cookie或者隱藏域中,cookie中若不加密很可能會被別人篡改,同理隱藏域也是可以被篡改,在一些介面訪問時會讀取這些資料,造成他人資料洩露。

解決:

重要資料全部放到後臺伺服器上存取,前臺只發送新增資訊。

10. 註釋漏洞

原因:

專案未上線時在js等靜態檔案上寫註釋,上線後使用本檔案,造成他人看到註釋內容,可能會洩露部分資訊。

解決:

上限時將js靜態檔案全部壓縮,壓縮會自動取消註釋並且節省空間。

11. API冪等性問題(手動實現redis-臨時token註解)

這實際上和4.中的冪等性問題一樣,是為了防止前臺過多請求,使用redis+註解的形式,使用redis的好處是可以有一個過期時間,防止無用資料過多!前臺可以通過JWT等方式把token資訊存到使用者客戶端。

1.初始化redis配置,通過在redisService中建立對應的方法,常用的註解如下:

stringRedisTemplate.opsForValue().set(key, value);

stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);

stringRedisTemplate.opsForValue().get(key);

stringRedisTemplate.delete(key);

通過注入一個對應型別的redisTemplate,用該物件操作資料。

需要注意的是expire()方法是設定redis的過期時間key方法,注入IOC容器

2.建立redisUtils

建立getToken,該方法呼叫redisService獲取token並存入reids並設定過期時間,findToken獲取redis中的token並返回boolean,如果獲取成功就抹去reids,注入IOC容器

3.建立自定義註解

4.建立自定義切面

切入點可以為execution表示式或註解,用表示式的話最好先判斷一下AOP攔截到方法上是否有自定義註解,在環繞通知或前置通知處通過反射獲取註解中的引數,呼叫對應tokenUtils的方法進一步判斷返回結果,如果成功獲取到token就執行原來的方法process()。

5.在進入某頁面前就呼叫getToken方法,或者在傳送請求前通過驗證方式獲取token

ps:需要注意的是,前臺可能是通過表單提交也可能是ajax請求,前者通過隱藏域將token資訊發到後臺,後者通過requestHeader傳參,註解需要加入引數型別判斷是表單還是ajax請求。

12. 開放介面的安全性設計

個人理解,實際上不管是不是開發的介面,都應該有token機制防範模擬請求攻擊和冪等性問題,但是開發性介面應該更加註重安全性,安全性是說必須保證是本系統去呼叫而不是其他系統(和單點登入安全相似),但是實現思想還是和之前一樣。

比如呼叫微信的開發介面時要帶上自己的token去訪問,而自己的token實際上是根據微信官方的要求,用自己appId和appSecret呼叫介面拿到的,呼叫獲取token介面會通過appId和appSecret共同通過演算法得出新token返回給使用者,同時更新到官方資料庫中。

出去安全性,官方要將相關appId、appSecret(appId不變,appSecret會變)以及對應的生成token資訊及時更新到資料庫中,當對方傳送請求時判斷請求中的token和最新token對比,另外,還要通過isFalg欄位判斷該appId是否允許可用。

這樣做即使appSecret被他人獲取,也能通過修改appSecret的方式重新改變 token。

和之前一樣,也可以用註解形式解決,因為開放介面相對不多,AOP靈活的特點符合此場景。

13. 個人理解的單點登入和異地登陸安全性解決方法

通過12中的例子,可以採用相同的辦法來解決SSO和異地登陸。

異地登入:

可以通過使用者名稱結合UUID等方式生成一個隨機token,並且這些token都會有一個過期時間,前臺通過jwt方式傳送到客戶端上,客戶端傳送請求時會帶上此token資訊,後臺則將token資訊存到redis中或資料庫中,甚至session也行。

實際上,後臺存到何處其實都行,主要考慮的是一些特殊情況,比如session過多會導致伺服器記憶體緊張,資料庫適用於訪問量小的介面情況,redis則適用於很多場景但是需要定期從記憶體對映到硬碟上。

當第一次訪問時,因為本地沒有 token,伺服器會產生一個新的token儲存到後臺,把新token傳送給客戶端。當A使用者登入後B使用者登入,因為之前的token還在,或者還未到期,此時就會發送一個不帶token的請求去獲取token,伺服器生成新的token發給B,更新伺服器上的token資訊,同時A使用者會出現重新登陸問題。

所以出現這種情況時,客戶端獲取token時可以順便將自己的IP(手機是動態IP可能不行)或者裝置號(可行,但是需要獲取到許可權)一起存到伺服器端,伺服器端收到未帶有token的請求時就可以大概率判斷出是不是本人來執行提示操作。

單點登入:

和異地登陸類似,多臺伺服器只要通過查詢token方式就行,問題在於多臺伺服器上的資料同步問題,如果是將token存到了session中,同步的就需要是seesion,如果是存到了redis中,直接讓多個伺服器連同一個redis就行,如果是資料,同理也是查詢同一個庫。如何同步問題辦法有很多,可以通過reids,mysql等方式,但是最好的辦法還是通過redis進行存取。

伺服器只要token資料同步,執行相同的token判斷登陸程式,單點登入問題就自然解決了。

14.URL漏洞

原因:

傳輸資料時如果url上帶有特殊字元,比如=+/?等,傳輸時如果不做特殊處理時會丟失資訊,以httpClient為例,A傳給B的資料沒有引數中的特殊字元進行處理,B最後得到的資料就會是錯誤的。

解決:

引數中的特殊字元進行編碼encode,使用java預設的編碼工具就行,不要對整個引數編碼。

15.對稱加密

對稱加密是雙方約定使用同一把祕鑰進行加密和解密,常見的如DES、AES等。

優點為加密解密相對快速省時間,缺點為有洩露風險。

因為加密解密是同一把祕鑰,在一些C/S應用就很有可能被黑客反編譯出原始碼,拿到加密的祕鑰之後就可以破解其他人的加密資訊。

使用場景:伺服器內部之間呼叫,如伺服器用httpClient、RPC通訊。

16.非對稱加密

非對稱加密一般是說利用一對公鑰加密,私鑰解密,常見的如有RSA。

優點為相對安全,缺點為解析費事效率低。

祕鑰存到伺服器上黑客難以獲取到,公鑰存到客戶端,即使被破解也無法解析出原來的資訊。開發人員提前用utils類生成一對公鑰私鑰儲存到伺服器上,之後用utils進行加密解密。

還有最近流行的Bcrypt屬於特殊的非對稱加密,每次加密的結果都不一樣,但是可以通過和結果對比確定是否正確。

使用場景:C/S結構的專案,如APP,PC客戶端

17.數字簽名

單項加密常用於數字簽名,通常都是傳遞引數+時間戳+固定鹽生成一個數字簽名,傳送時攜帶數字簽名,服務端只需要驗證數字簽名是否相等就能得知內容是否被篡改。

MD5加密特點是特定字串只能生成相同的結果,最適合數字簽名。

數字簽名是最實用、必備的的加密手段。

使用場景:各種包含加密資料的請求

18.支付類介面安全性問題(基於令牌實現的介面呼叫)

在前臺傳輸金額和商品資訊以及使用者id不能保證安全!因為在傳送到第三方介面的時候,很有可能會被第三方抓包或篡改資料。

採用支付令牌方式解決,這裡只討論將資料隱藏起來。

前臺在呼叫第三方介面時先將引數傳給本伺服器後臺getPayToken介面,伺服器後臺將關鍵引數進行確認,比如使用者id,金額等,判斷無誤後,通過伺服器呼叫第三方介面,拿到第三方介面返回的token資訊給前臺,前臺再拿token去訪問第三方介面,這樣就完成了資料的隱藏,防止黑客中途修改資訊。

ps:前臺傳資料給後臺時,價格類資訊必須在後臺查詢資料庫進行對比,必須以資料庫中的資料為主,商品id這類資訊即使被修改,實際上也無法達成篡改目的。

最終第三方支付介面也會回撥付款引數,後臺最後還要對比價格保證安全性。

19.https請求

https請求預設443埠,和http不是一個協議,預設會把帶的引數進行加密。

想要使用https請求需要為自己域名申請一個SSL證書,如阿里雲域名可以免費使用1年。

htttps相對安全,谷歌瀏覽器18年以將https作為預設協議,沒有帶有證書的網站都會顯示不安全。

微信小程式必須是https協議,搜尋引擎對https優先收錄,外網對映工具自帶https協議。

安全證書可以分配到nginx、tomcat、apach等伺服器上,具體參考官方手冊。

20.Https請求過程

1.客戶端使用https請求訪問擁有SSL證書的域名,伺服器生成一對公鑰和私鑰,把公鑰和相關證書資訊返回給客戶端,證書中包含一些加密演算法,頒發機構,公鑰,有效期等。

2.如果證書有效,客戶端就生成一個隨機數,然後用公鑰將此隨機數加密後傳送給伺服器。

3.伺服器用私鑰去解密資料,拿到隨機數,用該隨機數當成金鑰去加密一條資訊後返回給客戶端。

4.客戶端拿到加密資訊,用隨機數去解密,如果和之前結果一樣,之後雙方就會用此金鑰(隨機公鑰)進行加密資訊。

ps:https的過程是先用非對稱加密,之後用對稱加密,所以是https是一個混合加密模式。因為客戶端較多而且每個客戶端都要傳送很多請求,非對稱加密效率較低,需要用效率高德對稱加密。