如何做一次相对安全的登录

如何做一次相对安全的登录

如何做一次相对安全的登录

在我大学学习做开发的这几年,其实遇到的最多的就是 登录注册

最开始我想,无非就是前端把 用户名或是手机号密码 放在 AJAX 请求里,传到后端,放到数据库里一存 ...

但是其实这起中还有几个值得警戒的坑点:

  1. 一定不要在数据库中明文存储密码!
  2. 登录请求一定不要用 GET 方法,将字段编码进 URL 地址中,因为这同样是明文暴露
  3. 如果你不注意配合使用 HTTPS 协议,那么你请求中的数据很可能被恶意监听、截获

接下来就让我们来看看怎么做才能使用户登录、注册的过程更加可靠。

密码变哈希

我其实更愿意称「密码」为「口令」,汉语之中「密码」的 「密」其实是因为 「此时只有你知道」,所以对他人而言是密码,但一旦此字符串被他人知晓,所谓的「密」就不存在了。

在数据库中存储口令时,应该对口令做一次 「不可逆的加密」。密码学界有什么「目前」不可逆的加密算法呢?最出名的就要数 MD5 的各类衍生算法了,例如我最常用的就是 SHA-256 单向哈希算法

SHA-256 是指:将你的密码经过计算后得到一个长度为 256 bits 的哈希字符串,你无法通过哈希值本身去反向解析得到输入。

我们打个比方:一个舞蹈演员摆的姿势是「明文」、一道角度很偏的光线是「加密算法」,最后生成了一个奇形怪状的投影,即「计算后的哈希值」,试想你能通过这个投影想象出是什么姿势么?显然很难,想要生成一模一样的投影,还是得摆出原来的姿势。

我们在验证用户登录的口令输入时也可以用此思路:

  1. 首先用户进行了注册:
Input password: 123456
Generated Hash: e10adc3949ba59abbe56e057f20f883e

Generated Hash 存储在用户表的password字段中

  1. 下一次用户登录时输入密码 123456 ,再次计算发现和数据库中存储的 Hash 值匹配一致,判定登录成功。

    可是 ... 凭什么Hash值一致即可?难道 SHA-256 的 Hash 计算不会发生碰撞么?

    答案是: 你几乎不可能碰撞到。

    即找到另一种密码能计算后再得到 e10adc3949ba59abbe56e057f20f883e的几率无限接近于 0

    你随机取一个字符串,会有一个随机的哈希值。那么一个随机哈希等于你需要的哈希的概率是多少呢?很好算得是 $2256$。如果全宇宙的原子在 $1023$ 的量级的话。$2256$ 大概是 $1053$ 个宇宙中的原子那么多。

    所以随机碰撞几乎不可能成功。

另外为了避免骇客暴力破解,后端服务必须做限频处理。
故而只要用户保护好自己的密码,是没有人能够通过猜密码来偷偷登录他的帐号的,即使是网站的管理员,也没法通过调看数据库来知晓你的密码。

Session 并不是 HTTP 协议上的概念,而是一种服务器开发框架上的概念。它可以开辟一小段内存,并且这段内存有唯一标识,并且其他人无法访问到你的这段内存。看起来是很好的解决方案,但实际开发中,Session 有一些不容忽视的问题。

**1. 因为两次 HTTP 请求没有任何关联,那么服务器怎么确定该用户对应的是那一段内存呢?**唯一的办法就是使用 Cookie。

  • 在新用户输入密码正确,登录成功时,开辟新的内存,同时在 Cookie 中设置一个新的字段 Session-ID,这就算是该用户的临时标识符,而下次该用户再请求就会带上该 Cookie,服务器就可以根据其中的 Session-ID 找到对应的内存。

  • 而当用户退出登录时,清除 Cookie 中的 Session-ID 即可。

    虽然 Session 服务器概念的身份听上去很安全,但如何映射出这段内存,实际上是客户端概念 Cookie 来执行的,坏蛋还很容易通过篡改 Cookie 中的 Session-ID 来伪造用户身份 ...

2. 有些骇客可能会不停通过空 Cookie 来请求服务器,不停开辟内存,造成服务器内存泄漏 这样严重的安全问题。

3. Session 会让服务变得有状态,那么一旦服务器遇到什么问题宕机或者重启、负载均衡时请求过程经手多个服务器,也可能造成登录状态的丢失。

令牌在手准你走

为了保证只有一个验证关卡,不在其他任何地方允许读取数据库中用户的登录敏感信息,不妨引入「令牌」的机制。而且令牌和密码、口令不同,口令随时随地都有效,但令牌会有时限,大约 30 分钟到 2 个小时左右。

古时候将军外出打仗都会带着虎符的 A 面,皇帝要给他下达命令都会派使者拿着虎符的 B 面去和他做校验。

古人的智慧也可为我们所用:使用用户名和密码换取令牌,前端保存好它后就可以更新为「已登录」,之后所有的请求都带上它,遇到需要登录才可使用的服务,后端验证此令牌即可。

那么令牌 A 面中都有什么?

  1. 📖️ 明文的信息:例如 用户 ID、用户名和用户权限等级
  2. ⏰️ 令牌过期时间
  3. 🔐️ 数字签名:通过服务器的**「私钥」** 加密明文后生成一个加密字符串

数字签名的**「公钥」**是公开的,验证令牌时就是 用公钥去解密数字签名,如果解密结果刚好与明文相同,则说明令牌有效。

令牌的解密过程完全可以是本地化的,因为只需要公钥即可。例如用户在前端如果想及时更新状态,查看自己令牌是否过期,可以使用公钥用 JS 在本地直接做校验。

如果你对公钥、私钥这些 RSA 非对称加密算法的概念,请先去知乎 补补课 呀!

令牌存在 Cookie 中,在现代浏览器中也是 较为安全 的,因为它们大多都禁止在 JS 直接访问和修改 Cookie,更何况,光拿到令牌和公钥是不够的,最多维持一个令牌的有效周期,且令牌上还有严格的权限等级,要想完全掌控这个登录系统,除非你能拿到私钥。

但这就代表 Token 是完美的解决方案了么?当然不是,令牌也存在「收不回」的问题:

即一个系统是永远无法收回已经颁发后的令牌权限,诚如皇帝派去的使者,很难火速追上,在路上把他叫停。

但是总的来说,「JWT 令牌法」是目前最流行的登录解决方案,很多支付级别的应用安全要求那么高,也同样是用的这样的方案,它们给出的思路就是:

一个应用越重要,那么它的安全令牌有效时限就应该越短,这样骇客想进行窃取行动就会非常困难了。

# web开发 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×