每个网站、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; }
五、后端代码
略。按前面的分析思路来写即可