Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day36】—— 实战那些事儿1

车票

  • 面试题1:你们是怎样保存用户密码等敏感数据的?

  • 面试题2:怎么控制用户请求的幂等性的?

  • 面试题3:你们是如何预防SQL注入问题的?

  • 每日小结


本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识集合容器并发编程JVMSpring全家桶MyBatis等ORMapping框架MySQL数据库Redis缓存RabbitMQ消息队列Linux操作技巧等。

面试题1:你们是怎样保存用户密码等敏感数据的?

======================================================================================

本题回答参考朱晔的《Java业务开发常见错误100例》

在这里插入图片描述

我们知道,用户名、密码、身份证等都属于用户敏感信息,其中最敏感的数据恐怕就是用户的密码了。黑客一旦窃取了用户密码,就可以登录进用户的账号,消耗其资产、发布不良信息等;更可怕的是,有些用户至始至终都是使用一套密码,密码一旦泄露,就可以被黑客通过撞库来登录全网各大平台,嘿嘿嘿。

为了防止密码泄露,最重要的原则是不要在数据库保存用户原始密码

大家经常说,不要明文保存用户密码,应该把密码通过 MD5 加密后保存。这的确是一个正确的方向,但这个说法并不准确。

首先,MD5 其实不是真正的加密算法。所谓加密算法,是可以使用密钥把明文加密为密文,随后还可以使用密钥解密出明文,是双向的。而 MD5 是散列、哈希算法或者摘要算法。不管多长的数据,使用 MD5 运算后得到的都是固定长度的摘要信息或指纹信息,无法再解密为原始数据。所以,MD5 是单向的。最重要的是,仅仅使用 MD5 对密码进行摘要,并不安全。

比如,使用如下代码在保持用户信息时,对密码进行了 MD5 计算:

UserData userData = new UserData();

userData.setId(1L);

userData.setName(name);

//密码字段使用MD5哈希后保存

userData.setPassword(DigestUtils.md5Hex(password));

return userRepository.save(userData);

通过输出,可以看到密码是 32 位的 MD5:

“password”: “325a2cc052914ceeb8c19016c091d2ac”

然后,再去破解网站试一下这个 MD5,就可以得到原始密码是 salt,也就知道了盐值是 salt

在这里插入图片描述

其实,知道盐是什么没什么关系,关键的是我们是在代码里写死了盐,并且盐很短、所有用户都是这个盐。这么做就会有三个问题:

  • 因为盐太短、太简单了,如果用户原始密码也很简单,那么整个拼起来的密码也很短,这样一般的 MD5 破解网站都可以直接解密这个 MD5除去盐就是原始密码了。

  • 相同的盐,意味着使用相同密码的用户 MD5 值是一样的,知道了一个用户的密码就可能知道了多个

  • 黑客也可以使用这个盐来构建一张彩虹表,也就是字典表,虽然会花不少代价,但是一旦构建完成,所有人的密码都可以被破解。

所以,最好是每一个密码都有独立的盐,并且盐要长一点,比如超过 20 位。

第二,虽然说每个人的盐最好不同,但也不建议将一部分用户数据作为盐。比如,使用用户名作为盐:

userData.setPassword(DigestUtils.md5Hex(name + password));

如果世界上所有的系统都是按照这个方案来保存密码,那么 root、admin 这样的用户使用再复杂的密码也总有一天会被破解,因为黑客们完全可以针对这些常用用户名来做彩虹表。所以,盐最好是随机的值,并且是全球唯一的,意味着全球不可能有现成的彩虹表给你用。

正确的做法是,使用全球唯一的、和用户无关的、足够长的随机值作为盐。比如,可以使用 UUID 作为盐,把盐一起保存到数据库中:

userData.setSalt(UUID.randomUUID().toString());

userData.setPassword(DigestUtils.md5Hex(userData.getSalt() + password));

并且每次用户修改密码的时候都重新计算盐,重新保存新的密码。

需要注意的是,这么做虽然黑客已经很难通过彩虹表来破解密码了,但是仍然有可能暴力破解密码,也就是对于同一个用户名使用常见的密码逐一尝试登录。因此,除了做好密码哈希保存的工作外,我们还要建设一套完善的安全防御机制,在感知到暴力破解危害的时候,开启短信验证、图形验证码、账号暂时锁定等防御机制来抵御暴力破解

那么姓名和身份证又是怎么保存的?

我们把姓名和身份证,叫做二要素。

现在互联网非常发达,很多服务都可以在网上办理,很多网站仅仅依靠二要素来确认你是谁。所以,二要素是比较敏感的数据,如果在数据库中明文保存,那么数据库被攻破后,黑客就可能拿到大量的二要素信息。如果这些二要素被用来申请贷款等,后果不堪设想。

之前我们提到的单向散列算法(MD5),显然不适合用来加密保存二要素,因为数据无法解密。这个时候,我们需要选择真正的加密算法。可供选择的算法,包括对称加密非对称加密算法两类。

  • 对称加密算法:是使用相同的密钥进行加密和解密。使用对称加密算法来加密双方的通信的话,双方需要先约定一个密钥,加密方才能加密,接收方才能解密。如果密钥在发送的时候被窃取,那么加密就是白忙一场。因此,这种加密方式的特点是,加密速度比较快,但是密钥传输分发有泄露风险。

在这里插入图片描述

  • 非对称加密算法:或者叫公钥密码算法。公钥密码是由一对密钥对构成的,使用公钥或者说加密密钥来加密,使用私钥或者说解密密钥来解密,公钥可以任意公开,私钥不能公开。使用非对称加密的话,通信双方可以仅分享公钥用于加密,加密后的数据没有私钥无法解密。因此,这种加密方式的特点是,加密速度比较慢,但是解决了密钥的配送分发安全问题。

在这里插入图片描述

但是,对于保存敏感信息的场景来说,加密和解密都是我们的服务端程序,不太需要考虑密钥的分发安全性,也就是说使用非对称加密算法没有太大的意义。我们一般会使用对称加密算法来加密数据。常见的对称加密算法有:DES3DESAES

在这里插入图片描述


在这里插入图片描述

课间休息,欣赏一下来自咱们群里同学的搬砖工地附近,坐标:成都 太古里

作者:唐伯虎点香烟


面试题2:怎么控制用户请求的幂等性的?

==================================================================================

幂等性:对于同一笔业务操作,不管调用多少次,得到的结果都是一样的。

当然,有些操作是天然幂等的,如:

  • 查询操作:查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作;

  • 删除操作:删除操作也是幂等的,删除一次和多次删除都是把数据删除。

后台控制幂等性的几种途径:

  1. 设置唯一索引:防止新增脏数据

比如支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可);

  1. token机制:防止页面重复提交

原理上通过session token来实现的(也可以通过redis来实现)。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端

服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

  1. 悲观锁

获取数据的时候加锁获取。

select * from table_xxx where id=‘xxx’ for update;

注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的;悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用;

  1. 乐观锁

乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。乐观锁的实现方式多种多样,可以通过version或者其他状态条件判断:

  • 通过版本号实现

update table_xxx set name=#name#,version=version+1 where version=#version#;

  • 通过条件限制

update table_xxx set avai_amount=avai_amount where avai_amount >= 0

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。

面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!

  • 通过条件限制

update table_xxx set avai_amount=avai_amount where avai_amount >= 0

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。

面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!

[外链图片转存中…(img-uyDo8kDf-1714362679502)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值