每個網站、APP都幾乎必然有其管理后台,其中管理的內容則是公司的核心技術財產。而登錄模塊則是這扇大門,其安全的重要性可想而知。我們知道,功能越多,安全性就會越低,所以我們有必要重新審視一下,管理后台的登錄界面到底需要些什么功能。
一、功能模塊的取舍
1、基本的賬號密碼登錄。這個無可避免是必然需要的了。
2、圖片驗證碼。驗證碼的目的是為了阻止機器人暴力撞庫,作為管理后台很有必要,而且是要每次登錄請求都需重新驗證。
3、填完用戶名或密碼時,Ajax實時驗證。這個功能常見於一些自動管理后台的注冊模塊,用於驗證用戶名是否已被占用。但此功能通常會導致不需經過驗證碼驗證,從而使得暴力撞庫有機可乘。
4、記住我選項。這是一個使用cookie記住登錄用戶的功能,使用戶下次再來時可以不需要再登錄即可通過驗證。但cookie必然需要記錄 用戶ID或用戶名 相關的信息,存在瀏覽器中,有一定的CSRF攻擊風險和信息泄漏風險。
5、找回密碼功能。這是一個高危功能,無論是邏輯疏漏還是安全不嚴謹,都會導致賬號的失竊。參考1月份支付寶找回密碼的危機。所以建議做法是,公司文檔保存相關的賬號密碼信息,如遇實在無法登錄,則找技術人員進入數據庫修改密碼(加密后)。
6、注冊功能。這個不要做在外面,在后台的功能里加一個添加用戶會安全很多。
7、第三方登錄。如QQ登錄、微信登錄?不需要,大家都知道QQ很容易被盜號,不宜作為安全性要求高的系統的登錄入口。微信則需要拿手機出來掃碼,不如直接輸入密碼來得方便,另外它還需要申請微信公眾號以及500塊每年的公眾號認證費用。
綜上,得出一個夠用、安全的管理后台的登錄界面
二、安全功能
1、驗證碼安全。以AJAX提交為例,每次嘗試登錄后,無論是否登錄成功,后端都要注銷當前驗證碼SESSION,前端JS刷新驗證碼。后台要注銷SESSION是以免黑客屏蔽JS導致驗證碼只需一寫次,從而導致爆庫。
2、網絡傳輸安全。最好使用https加密,以免網絡傳輸過程泄露賬號密碼,如在咖啡店等他人WIFI環境。如果沒有使用HTTPS,則應該在前端JS加密登錄名和密碼,后端再解密。因為JS是明文的,所以要使用非對稱性加密(如RSA),JS使用公鑰加密,服務端使用私鑰解密。甚至對JS文件本身也可以作一些加密壓縮。為什么登錄名也要加密呢?還是避免信息泄露,以免別人根據登錄名猜出密碼。
3、登錄成功時重新生成SESSION_ID。主要是為了防止固定會話ID的CSRF攻擊。
三、登錄日志
知己知彼,戰斗才能勝利。上面這些功能和安全,都是一些通用的防守攻擊套路。但敵人在暗我在明,敵人什么時候派出過特務,什么時候發出過攻擊,發起了什么樣的攻擊?僅通過上面的功能,我們無從得知。所以,我們還需要一個監控器--登錄日志。
然后這個登錄日志,我們需要記錄些什么東西呢? 登錄名、是否成功、IP地址、時間。但是,這還不夠,這樣我們只能分析到了是誰有攻擊我們,但是分析不到他是通過什么方式來攻擊。那還要記錄什么呢?URL地址(含GET數據)、POST數據。但需要注意的是,我們登錄時的密碼也在POST數據里,切不可將密碼存儲在登錄日志里,即使是RSA加密過的也不行,應以***星號代替,否則這和明文存儲密碼沒什么差別。
四、前端代碼
前端代碼的要點是登錄時RSA加密賬號密碼,使用的是 jsencrypt.js 庫,Ajax提交表單用的是 jquery.form.js 。核心代碼如下,需要注意的是,ajaxForm接受的這兩個回調函數,參數名是固定的無法修改,修改表單數據用的是formData,提交成功回調的結果名是responseText。標紫色的兩個變量是后台輸出的模板變量。
//AJAX提交登錄表單 $(function(){ var formSubOpt = { beforeSubmit: encodeForm, success: formRes }; $("#loginform").ajaxForm( formSubOpt ); }); //提交成功 function formRes(responseText){ //參數名要為這個 // console.log(responseText); ajaxAlerts(responseText); //提示 if(responseText.code<0){ changeVer(); $("input[name=ver]").val(""); } if(responseText && responseText.code==0){ setTimeout(function(){ location.href = "{$toURL}"; },750); } } //RSA加密賬號密碼 var RSApubKey = "{$RSApubKey}"; //console.log(RSApubKey); function encodeForm(formData){ // console.log(formData); //這是要提交的參數 var crypt = new JSEncrypt(); crypt.setKey( RSApubKey ); var USER = crypt.encrypt( $("input[name=name]").val() ); var PW = crypt.encrypt( $("input[name=psw]").val() ); // console.log( PW ); formData[0].value = USER; formData[1].value = PW; }
五、后端代碼
略。按前面的分析思路來寫即可