深入学习PHP中的SESSION(二)

今天的学习内容没有太多的代码,主要还是以理论经验为主,当然,主要的依据还是来源于 PHP 官方文档中的说明。在日常的业务开发中,SESSION 安全一直是我们最主要也是最关心的内容,不管是面向前端的用户还是面向后台的管理员,SESSION 泄露可能都会带来严重的问题。这是我们需要重点的考虑的内容之一。

会话安全

从上篇文章中我们了解到,其实 SESSION 之所以能够实现,最核心的内容就是那个 phpsessid ,也就是 session id 。这个就相当于是一个密钥,有了它,我们就可以随便登录我们的系统了。不少小伙伴一下就明白了,要么从 url 中获取,要么从 Cookie 中获取,这个 session id 总共也就这两种传递的形式。

没错,所以在正常情况下一般都不推荐使用 url 形式来传递这个 session id ,毕竟明文地在连接中简直是太方便获取到了。而 Cookie 的话,比较建议的是设置 Cookie 的 secure 和 http_only 这两个选项。 secure 的作用是让 Cookie 只能通过 https 来进行传输,而 http_only 则是让 Cookie 无法通过 JavaScript 读取,也就是 document.cookie 无法读取 Cookie 。为什么要有这两个操作呢?

HTTPS 在传输过程中是安全的,也就是说,在传输的过程中,数据是加密的,双方的证书都存在且匹配的情况下它们才会正确地编解码。这就避免了在数据传输过程中的窃取问题的发生。而不让 JavaScript 读取则是应对 XSS 攻击的有效方式,不管是存储型、反射型的 XSS ,核心特点都是在注入成功之后通过窃取 Cookie 来拿到当前帐号的登录权限。通过限制 JS 读取之后,也就无法拿到 Cookie 里面的内容,自然也不会泄露 session id 了。

除此之外,域信息、路径信息这些 Cookie 中的设置属性也最好都考虑应用上,对于跨域跨站的攻击都是有帮助的。

重新生成 session id

除了 Cookie 相关的设置外,每次请求或者说间隔一段时间就重新生成一个 session id 也是一种不错的安全保障机制。也就是说,利用我们上篇文章中学习到的 session_create_id() 或 session_regenerate_id() 这两个函数,在每次请求之后都调用一下,这样都会重新生成一个新的会话ID。就算其他人拿到了之前的 session id ,放到新的请求中也会失效。不过这个操作会有一定的垃圾成本,也就是会产生很多空的 session 文件或者数据。在上篇文章的演示中我们也看到过,重新生成 session id 之后,原来的文件或者数据项还存在,虽说里面是空的了,但还是会占用一定的空间。

注意,在使用 session_regenerate_id(); 时,要设置它的参数为 true ,也就是 session_regenerate_id(true); 这样,这个参数的意思就是删除老的 session id 内容。否则老的 session id 还是可以访问到数据的。

同理,现在很多网站会做自动登录,对于每次登录之后,最好是刷新你的 Token 或者是 session id ,而且过一段时间也要让用户重新登录一次。毕竟这些登录信息也是保存在客户端的 Cookie 中的。除了相关的 Cookie 安全设置之外,还要根据用户的登录环境比如 IP 地址、浏览器的变化来及时地让自动登录失效,保证用户的帐号安全。另外,注意要让 Cookie 和 SESSION 的过期时间保持一致,让它们一起过期。

安全选项配置

在 php.ini 中,有一个 session.use_strict_mode 选项,在默认情况下它是关闭的。如果开启它的话,就可以让会话模块禁止使用未初始化的 session id 。也就是说,它只接受由当前系统自己创建的有效的 session id ,而拒绝由用户自己提供的 session id 。

攻击者可以自行设置 Cookie 或者使用 JS 注入的方式来设置 session id 并进行会话攻击。启用 session.use_strict_mode 之后,可以阻止未经模块初始化的 session id 的执行。

上面说的是什么意思呢?比如我们传递一个不是由我们系统生成的 session id 。然后系统并没有严格地验证用户登录情况,而是以这个 session id 进行新的初始化并附上一些数据的话,那么这个攻击就成功了。

另外,如果确定我们当前的页面只是读取,比如说非登录页面。那么我们可以为当前的会话设置一个 read_and_close 属性,会话文件在读取完成之后会马上关闭。

session_start(['read_and_close'=>true]);

CSRF

对于 SESSION 来说,CSRF 是无法防范的,但在 PHP7.3 之后增加了 Cookie 中的 SameSite 设置,也就是同源站点信息的设置,可以有效地防范此类攻击。当然,对于 CSRF 更方便地还是增加随机 Token 来验证,这个功能各类框架都已经提供了,这里也就不在多说了。

核心重点功能的重验证

最后,我们再来说一个问题。大家在使用很多应用的时候都会发现一个问题,特别是在涉及金钱交易的时候,都会让我们每次都输入一个交易密码。这其实是通过业务的手段来保证安全的一种形式。也不一定是完全的在涉及到金钱交易的时候才应用,比如我们后台如果检测到了用户登录的异常,IP 地址变化、浏览器网络环境的变化,那么即使他登录成功了,也只能做一些常规性的操作,而在重要的操作时,比如发表新的文章、查看核心的数据、删除用户或数据、修改数据的时候,重新进行一次简单的密码验证,也能够有效地对我们的系统用户安全进行保障。

总结

对于 SESSION 的安全来说,Cookie 其实才是最重要的一环。对于这点来说相信大家不会有异议。要说最核心的问题那只能说是 HTTP 是无状态的,实在是没有别的方法能够在不同的页面之间共享数据。Cookie 配合 SESSION 这种方式是最经济实惠而且也是最方便的一种方案。只要做好了相应的防护,基本上在我们的日常开发中还是不会有太大的问题的。安全从来都没有绝对一说,只有相对的安全。即使是大如阿里、腾讯之类的公司,也无时无刻的不在应对各种网络攻击威胁。不断地提升自己,学习各类安全防范知识才是我们进一步提升的关键。

参考文档:

https://www.php.net/manual/zh/features.session.security.management.php