App一般都需要用户的登录操作。登录,就需要用到用户名和密码。为了安全起见,暴露明文密码的次数越少越好。这里有两个问题:1,怎么能最大程度避免泄露用户的密码呢?2,在登录后,app后端又怎么去验证和维持用户的登录状态呢?本文先讲述第一个问题--如何最大程度避免用户密码泄露。第二个问题放在下篇。
密码存储
- 用户密码必须不能明文存储。如果数据库泄露,那么所有用户的密码就会被泄露,存在非常高的安全风险。
- 用户密码不能直接hash存储。hash破解现在已经不是一件难事,方法有查表法、反向查表法、彩虹表等。
- 在存储用户密码的时候,一定要考虑的是加盐hash。所谓 盐(salt) 实际是一个随机字符串,在用户密码的前面后者后面加一个随机字符串,然后再hash,即使hash被破解,也能保证用户密码不丢失。加盐hash需要一些基本原则:
- 盐值永远不要重复使用;
- 每个用户的每一个密码都要使用独一无二的盐值;
- 用户每次创建帐号或更改密码时,密码应采用一个新的随机盐值;
- 盐值需要单独存储,或者固定盐值长度,然后固定放在 hash结果 的某个位置
- 盐值不要太短,建议32位以上
- MD5 、SHA1、SHA256、SHA512、RipeMD、WHIRLPOOL、SHA3等hash算法,都属于快速加密hash函数,他们的弱点在于,加密快,破解也快。建议使用 慢哈希函数 进行hash处理,如 PBKDF2 或 bcrypt的安全版本。 慢哈希函数 在hash的计算过程中,也会变慢,但是这个速度是可以根据参数调整的,建议慢hash时间调整到200ms-500ms,如果速度过慢,容易让系统遭受ddos攻击。
- 到这一步,系统还不是完全的安全。如果我们采用加密算法,比如 AES对称加密,对hash结果进行加密,那么只有知道秘钥的人才能解密出hash结果,然后才有机会对hash破解。但是,又引出了一个新问题:如何保证秘钥的安全性?这个密钥必须在任何情况下,即使系统因为漏洞被攻陷,也不能被攻击者获取。如果攻击者完全进入系统,密钥不管存储在何处,总能被找到。因此,密钥必须密钥必须被存储在外部系统,例如专用于密码验证一个物理上隔离的服务端,或者连接到服务端,例如一个特殊的硬件设备。
- 如果被拖库,数据发生丢失,我们要做的第一件事应该是:确定系统被暴露到什么程度,然后修复攻击者利用的的漏洞;在还没有完全发生什么事情时,需要尽快通知用户修改密码,甚至强制用户修改密码;如果数据库包括信用卡等信息,应该通知用户仔细检查近期账单甚至销掉这张信用卡。
密码传输
- 上面说的问题只是密码存储问题,实际上还要一个更严重的问题需要考虑,既密码的传输问题。用户在注册或者登陆时,需要将密码传递到服务端。但是要严禁明文传输密码。
- 建议的一种方案是,非对称加密算法对密码进行加密传输:
- 用户在传输密码之前,先像服务器请求一个公钥;
- 然后传输的密码是 使用 公钥加密的 密码,而这个加密之后的密码,只有用自己的私钥才能解开;
- 服务端使用私钥解开密码,然后加密存储。
- 这里面又涉及到两个问题:1是私钥的安全性存储;2公钥私钥对 是否需要对每一次登录或注册都重新生成一对? 从安全性来讲,后者显然会更好,而且公钥、私钥有过期时间。
- 具体的实现过程,这里不讲。
基本原则
- 永远不要告诉用户输错的究竟是用户名还是密码。
- 通用的提示:“无效的用户名或密码”。
好久没写文章了。希望以后能多学习一些东西,多写一些。