首页 > 开发 > Php > 正文

php session的锁和并发

2020-02-21 20:41:00
字体:
来源:转载
供稿:网友

本文分享PHP的session在使用过程中的锁和并发的问题,与之相关的现象有请求阻塞、session数据丢失、session数据读不到。

我登录不了了
某天,我准备登录我们一个后台系统,前去解决一个bug,在账户密码验证码都准确输入的情况下,我登录不上,经过多次实验发现主要有两个错误信息:

csrf验证失败 验证码错误【我对码神起誓我用半角输入了我看到的验证码,且顺序一致,无多加字符】

我们的系统
我们的系统是基于phalcon 2.0.8 开发的,如你所见,我们在表单域加入了防止csrf攻击的域。也启用了验证码。

<input type="hidden"   name="{{ security.getTokenKey() }}"  value="{{ security.getToken() }}"/><img src="/login/getCaptcha" id="img-captcha"/> 

我首先对这两个组件进行查阅,发现他们都是将数据存于session:

# phalcon/security.zep# Security::getToken()let session = <SessionInterface> dependencyInjector->getShared("session"); session->set(this->_tokenValueSessionID, token); $this->session->set('admin_get_captcha_action', $captcha);

然后我又查阅了我们session的实现,发现是将数据存储于redis的。

找啊找
什么问题导致我登录不上呢?既然是数据验证上出现问题,就从数据着手吧,我登陆我们测试环境的redis机器,执行 redis-cli monitor,然后走一遍登录流程,发现输出如下(意思意思):

GET sessionId 
GET sessionId 
SETEX sessionId 3600 csrf=xxxx 
SETEX sessionId 3600 captcha=abcd 

我们可以看到:

1、这里存在两次请求,一次是表单加载,一次是生成验证码的。
2、存在“并发”的情况,这两个请求应该是表单加载渲染后才请求验证码的,也就是session顺序应该是get->set->get->set,看起来怎么是并发请求了。
3、后面那个SETEX没有csrf的内容,也就是覆盖掉前面的数据了
整个世界都不好了,不过也稍微明白是什么问题了。什么问题呢,说来话长,要从PHP的session数据的存取说起。

php的session数据的存取
session的数据是经过编码成字符串存储在存储器【file、db、redis、memcache等】的,在我们使用session的时候,是什么时候去储存器取数据的?又是什么时候将数据写入存储器的?

这个问题的答案可能和一些朋友想的不一样,一个请求里面,PHP只会读取一次存储器,在session_start的时候,然后也只会写入一次存储器,在请求结束的时候,或调用session_write_close的时候,将数据刷回存储器,关闭session。

那么问题来了:

1、如果一个会话,同时出现两个读写session请求,没有保证获取1-写入1-获取2-写入2,同时没有cas版本管理机制的情况下,这些并发请求就会彼此读取不到对方的写入,最后写入的会把前面请求写入的session覆盖掉。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表