身份认证技术
身份认证技术是在计算机网络中确认操作者身份的过程而产生的有效解决方法。 计算机网络世界中一切信息包括用户的身份信息都是用一组特定的数据来表示的,计算机只能识别用户的数字身份,所有对用户的授权也是针对用户数字身份的授权。 如何保证以数字身份进行操作的操作者就是这个数字身份合法拥有者,也就是说保证操作者的物理身份与数字身份相对应,身份认证技术就是为了解决这个问题,作为防护网络资产的第一道关口,身份认证有着举足轻重的作用。摘自百度百科
我们可信赖的身份认证方式有以下三种:
基于信息秘密的身份认证:根据你所知道的信息来证明你的身份(what you know,你知道什么 ) ;基于信任物体的身份认证:根据你所拥有的东西来证明你的身份(what you have,你有什么 ) ;基于生物特征的身份认证:根据独一无二的身体特征来证明你的身份(who you are,你是谁 ) ,比如指纹、面貌等。
在网络世界中手段与真实世界中一致,为了达到更高的身份认证安全性,某些场景会将上面3种挑选2中混合使用,即所谓的双因素认证。
我们在此篇中暂时只讨论基于信息秘密的身份认证,如口令。我们知晓安全的登录身份认证不单单只是服务器端认证,还应当包含你的请求认证数据包的安全性。如果你的数据包在传输过程中被人截取,且能轻易破解,那么不管服务器端的认证做得如何好,都将无法起到很大的防护作用。在实际的渗透测试工作中,我们就会碰到很常见的一个点:即当我们进行登录门户的测试时,我们往往就会写出登录口令明文传输or使用弱加密传输,如Md5等的漏洞结论。
AES加密算法
前段时间工信部来我司进行安全检查,也对此问题提出了意见,他们给出了一个关于登录口令明文传输问题的解决方案为:使用AES加密传输。
什么是AES
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),是一种区块加密标准。
为了替代已被认可不安全的DES,AES应运而生,且目前被全球认可使用。DES之所以会被取代,是因为其使用的密钥为56位,比较容易被破解。而AES可以使用128、192、和256位密钥[1 Byte(字节)=8 bit(比特)],并且用128位分组加密和解密数据,相对来说安全很多。完善的加密算法在理论上是无法破解的,除非使用穷尽法。使用穷尽法破解密钥长度在128位以上的加密数据是较为不现实的,仅存在理论上的可能性。
AES现在广泛用于金融财务、在线交易、无线通信、数字存储等领域。
AES加密传输认证
在实际的协助问题修复中,我们会发现很多问题。公司研发无法很好地理解什么是AES加密,我们应该怎么做,去符合这样的一项要求。因此,我们需要提供一些更好的帮助。
认证逻辑
当我们尝试登录时,会先从服务器端获取随机生成的密钥,该份密钥在服务器端单次保有,长度应当是192位 or 256位。当我们获取到加密密钥后,会用此密钥将我们输入的口令进行加密,并将生成的密文传递给后端。需要注意的是,此时传递给服务器端的数据只有username和password,不能将密钥同时传递给服务器端。当服务器获取到密文时,会根据这次保有的密钥进行解密,获取到明文。当解密结束后,单次保有的密钥就失效了。下一次的登录认证将需要新的密钥。
代码实例
我们通过上面的流程图及逻辑解释能够大致了解到使用AES进行登录认证的过程。那么我们具体在代码中实现该如何去做。
前端AES加密实现
当我们想要在前端做加密,那么前端的加密算法是必不可少的。
1 | <script th:src="@{js/aes.js}"></script> |
前端加密实现很简单,但是我们需要用到一份aes的加密库,即上述中写的aes.js,我们可以在此处找到。
后端AES解密实现
当我们从前端获取到密文(注意:此处传递给服务器端数据只有密文,不能将前端获取到的密钥传递回后端)我们需要在后端解密密文。后端的解密相对简单,当然前提是你写好了AES方法。
1 | String pwd = AesUtil.aesDecryptContent(userPass, userKey); |
从上述的代码中,我们能发现,我们的方法都写在了AesUtil中,如果想要,我们可以在此处获取到。
AES密钥生成
AES根据其密钥长度分为三类:AES128、AES192、AES256。
那么我们的密钥应当是如何生成,这里给出一点参考的方法。
随机生成密钥
1 | public static void getKey() { |
使用指定的字符串生成秘钥
1 | public static void getKeyByPass() { |
byte数组转化为16进制字符串
1 | public static String byteToHexString(byte[] bytes) { |
登录认证
当我们通过上述的方式生成AES密文,并且成功将其传递到后端,我们就可以进行我们正式的登录认证了。这里我们要提到一个额外的点,账户信息的数据库加密存储。
注册的账户信息存储
当账户初次生成,即相当于是注册的过程,我们姑且将场景定为用户注册。当用户A进行了注册,A提交的数据为username和password,当数据传递到服务器端时,数据将会变成username和aes(password),即用户名和AES加密的密文。此时,服务器端先将密文转换成明文形式,服务器端将会生成一个真随机盐值salt,我们通过salt+password的形式进行哈希,这里推荐使用SHA256。此时,存储进数据库的账户信息字段为username、hash(password,salt)和salt。
登录的账户认证
我们通过上述的注册流程成功注册了一个账户。当用户A想要进行登录时,需要经过身份认证。认证的流程将会是如此。A提交username和password1,将username和aes(password1)传递给服务器端,此时,服务器端先将密文口令解密,再根据用户名在数据库中查找相对应的salt,通过hash(password1,salt)得到此时A输入口令相对应的哈希值,用此哈希值与数据库中存储的哈希值进行对比,如果哈希值等同,则表示用户输入的口令正确,则通过认证。
总结
通过上述的内容,我们能够完成一个大致的AES加密传输及用户的认证。我们需要注意的点如下:
- 登录传输使用AES加密
- 传递回服务器端的数据为用户名、AES加密密文,不包含密钥
- 数据库存储字段为用户名、口令哈希值、盐值
- 盐值应当是真随机的
- 口令哈希值为口令明文+盐值的哈希