Detailed explanation of session lock, concurrency, and coverage in PHP

小云云
Release: 2023-03-21 21:54:02
Original
1989 people have browsed it

This article mainly introduces you to the lock and concurrency of php session. Related phenomena include request blocking, session data loss, and session data cannot be read. Interested friends can refer to it. I hope it can help. to everyone.

I can’t log in
One day, I was going to log in to one of our backend systems to solve a bug, and I entered the account password verification code accurately. In this case, I cannot log in. After many experiments, I found that there are two main error messages:

  • ##csrf verification failed

  • Verification code error [I swear to the code gods that I entered the verification code I saw using half-width, and in the same order, without extra characters]

Our systemOur system is developed based on phalcon 2.0.8. As you can see, we added in the form field Domain to prevent CSRF attacks. Captcha is also enabled.

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

I first checked these two components and found that they both store data in the session:

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

Then I checked the implementation of our session, It is found that the data is stored in redis.

Looking and searchingWhat is the problem that prevents me from logging in? Since there is a problem with data verification, let’s start with the data. I logged in to the redis machine in our test environment, executed redis-cli monitor, and then went through the login process and found that the output is as follows (meaning):

  • GET sessionId

  • ##GET sessionId

  • SETEX sessionId 3600 csrf=xxxx

  • #SETEX sessionId 3600 captcha=abcd

    We can see:
1. There are two requests here, one is to load the form, and the other is to generate the verification code.

2. There is a "concurrency" situation. These two requests should request the verification code after the form is loaded and rendered. That is, the session sequence should be get->set->get->set. It seems Why are there concurrent requests?

3. The latter SETEX does not have the csrf content, which means it overwrites the previous data.

The whole world is not good, but I understand a little bit what the problem is. What's the problem? It's a long story, starting with PHP's session data access.


Access to session data in php

The session data is encoded into a string and stored in the memory [file, db, redis, memcache, etc.]. When we use session, when do we go to the storage to get data? When is the data written to memory? The answer to this question may be different from what some friends think. In a request, PHP will only read the memory once, during session_start, and then it will only write to the memory once, at the end of the request. , or when session_write_close is called, the data is flushed back to the memory and the session is closed.

Then the question comes:

1、如果一个会话,同时出现两个读写session请求,没有保证获取1-写入1-获取2-写入2,同时没有cas版本管理机制的情况下,这些并发请求就会彼此读取不到对方的写入,最后写入的会把前面请求写入的session覆盖掉。
2、如果请求是串行的,像登录页面的表单和验证码,也有可能前面的请求已经输出内容了,但是session还没写入,后面的请求就已经发起了。
锁与不锁
解决这种资源的并发一般会通过锁或版本管理来处理。但是版本管理我看不到好的方法。就聊聊锁吧。

其实锁是不大适合,有弊端的。

php的session,默认是用文件存储的,在打开session的时候,会对文件加独占锁,这样,其它请求就无法获取锁了,只能等待直到前面的锁解了。

这样保证了 读取-写入,读取-写入的顺序。

其它存储器,例如mysql,可以借助select for update进行行锁。redis可以通过一个自增键,返回1的获取到锁等来实现。

这个实现的话,对数据流来说很理想,但是,对于目前这种页面大量应用ajax的情况,所有请求排队处理,将大大加大页面展现的耗时,甚至出现请求超时等不可用故障。

没有解决的解决不建议过多使用session,其一次读取一次写入的机制所引发的问题,会造成坑的存在。
在模版渲染前,或请求输出前调用session_write_close

# 立刻回写session,避免session覆盖
$eventManager = $this->view->getEventsManager();
if (!$eventManager) { 
  $eventManager = new Manager();
  $this->view->setEventsManager($eventManager);
}
$eventManager->attach("view:afterRender",function(){
  session_write_close();
});
return $this->view; 
if($login) { 
  # 立刻回写session,避免session读取不到
  $eventManager = $this->dispatcher->getEventsManager();
  if (!$eventManager) {
    $eventManager = new Manager();
    $this->dispatcher->setEventsManager($eventManager);
  }
  $eventManager->attach(&#39;dispatch:afterDispatchLoop&#39;,function(){
    session_write_close();
  });
  return $this->response->setHeader(&#39;Location&#39;, &#39;/&#39;);
}
Copy after login

相关推荐:

php中session锁防止阻塞请求的实例分析

The above is the detailed content of Detailed explanation of session lock, concurrency, and coverage in PHP. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template